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.

2758 lines
83 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+============================================================================
  3. //
  4. // vol.cxx
  5. //
  6. // This file implements the CVolume class. This class maintains all activities
  7. // for a volume, such as the the log, log file, and deletions manager classes.
  8. //
  9. //+============================================================================
  10. #include <pch.cxx>
  11. #pragma hdrstop
  12. #include "trkwks.hxx"
  13. #include <dbt.h>
  14. #define THIS_FILE_NUMBER VOL_CXX_FILE_NO
  15. //+----------------------------------------------------------------------------
  16. //
  17. // CVolume::AddRef
  18. //
  19. //+----------------------------------------------------------------------------
  20. ULONG
  21. CVolume::AddRef()
  22. {
  23. long cNew;
  24. cNew = InterlockedIncrement( &_lRef );
  25. //TrkLog(( TRKDBG_VOLUME, TEXT("+++ Vol %c: refs => %d"), VolChar(_iVol), cNew ));
  26. return( cNew );
  27. }
  28. //+----------------------------------------------------------------------------
  29. //
  30. // CVolume::Release
  31. //
  32. //+----------------------------------------------------------------------------
  33. ULONG
  34. CVolume::Release()
  35. {
  36. long cNew;
  37. cNew = InterlockedDecrement( &_lRef );
  38. //TrkLog(( TRKDBG_VOLUME, TEXT("--- Vol %c: refs => %d"), VolChar(_iVol), cNew ));
  39. if( 0 == cNew )
  40. delete this;
  41. return( cNew >= 0 ? cNew : 0 );
  42. }
  43. //+----------------------------------------------------------------------------
  44. //
  45. // CVolume::Initialize
  46. //
  47. // Initialize CVolume and open a handle to the volume itself, but nothing
  48. // more (e.g. don't open the log or verify volume IDs). The remainder of
  49. // the initialization will occur later the first time ReopenVolumeHandles
  50. // is called. This gets that heavy IO work out of the service initialization
  51. // path.
  52. //
  53. //+----------------------------------------------------------------------------
  54. BOOL
  55. CVolume::Initialize( TCHAR *ptszVolumeName,
  56. const CTrkWksConfiguration * pTrkWksConfiguration,
  57. CVolumeManager *pVolMgr,
  58. PLogCallback * pLogCallback,
  59. PObjIdIndexChangedCallback * pObjIdIndexChangedCallback,
  60. SERVICE_STATUS_HANDLE ssh
  61. #if DBG
  62. , CTestSync * pTunnelTest
  63. #endif
  64. )
  65. {
  66. HANDLE hFile = NULL;
  67. // const CVolumeId volNULL;
  68. // CVolumeId volidVolume;
  69. NTSTATUS status;
  70. BOOL fSuccess = FALSE;
  71. _iVol = -1;
  72. memset( &_volinfo, 0, sizeof(_volinfo) );
  73. // Save the volume name, without the trailing whack
  74. // Volume names are in the form \\?\Volume{guid}\
  75. _tcscpy( _tszVolumeDeviceName, ptszVolumeName );
  76. TrkAssert( TEXT('\\') == _tszVolumeDeviceName[ _tcslen(_tszVolumeDeviceName)-1 ] );
  77. _tszVolumeDeviceName[ _tcslen(_tszVolumeDeviceName)-1 ] = TEXT('\0');
  78. // Save the inputs
  79. _pTrkWksConfiguration = pTrkWksConfiguration;
  80. _pVolMgr = pVolMgr;
  81. _pLogCallback = pLogCallback;
  82. _ssh = ssh;
  83. _hdnVolumeLock = NULL;
  84. _fVolumeDismounted = _fVolumeLocked = FALSE;
  85. IFDBG( _pTunnelTest = pTunnelTest; )
  86. __try // __except
  87. {
  88. // Create critical sections
  89. _csVolume.Initialize();
  90. _csHandles.Initialize();
  91. _fInitialized = TRUE;
  92. Lock();
  93. __try // __finally
  94. {
  95. _VolQuotaReached.Initialize();
  96. // Open the volume (not a directory in the volume, but the volume itself).
  97. // We'll use this to do relative-opens by object ID
  98. status = OpenVolume( _tszVolumeDeviceName, &_hVolume );
  99. if (!NT_SUCCESS(status))
  100. TrkRaiseNtStatus(status);
  101. // Initialize, but don't start, the objid index change notifier. When started,
  102. // this will watch for adds/deletes/tunnels/etc. in the index.
  103. _ObjIdIndexChangeNotifier.Initialize( _tszVolumeDeviceName,
  104. pObjIdIndexChangedCallback,
  105. this );
  106. fSuccess = TRUE;
  107. }
  108. __finally
  109. {
  110. Unlock();
  111. }
  112. }
  113. __except ( BreakOnDebuggableException() )
  114. {
  115. TrkLog(( TRKDBG_ERROR, TEXT("Failed initializaion of volume %s (%08x)"),
  116. ptszVolumeName, GetExceptionCode() ));
  117. if( TRK_E_VOLUME_NOT_DRIVE != GetExceptionCode() )
  118. {
  119. TrkReportInternalError( THIS_FILE_NUMBER, __LINE__,
  120. GetExceptionCode(), TRKREPORT_LAST_PARAM );
  121. }
  122. }
  123. return fSuccess;
  124. }
  125. //+----------------------------------------------------------------------------
  126. //
  127. // CVolume::SetLocallyGeneratedVolId
  128. //
  129. // Generate a volume ID and set it on the volume. If we're in a domain,
  130. // this will later get replaced with a volume ID from trksvr.
  131. //
  132. //+----------------------------------------------------------------------------
  133. void
  134. CVolume::SetLocallyGeneratedVolId()
  135. {
  136. NTSTATUS status = STATUS_SUCCESS;
  137. // Ensure the volume is writeable
  138. RaiseIfWriteProtectedVolume();
  139. _fDirty = TRUE;
  140. // Create the ID
  141. // Call _volinfo.volid.UuidCreate()
  142. RPC_STATUS rpc_status = GenerateVolumeIdInVolInfo();
  143. if( RPC_S_OK != rpc_status )
  144. {
  145. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create a local volid for new volume") ));
  146. TrkRaiseWin32Error(rpc_status);
  147. }
  148. // Set the ID on the volume.
  149. status = SetVolIdOnVolume( _volinfo.volid );
  150. g_ptrkwks->_entropy.Put();
  151. if( NT_SUCCESS(status) )
  152. TrkLog(( TRKDBG_VOLUME, TEXT("Locally generated a new volid for %c:, %s"),
  153. VolChar(_iVol), (const TCHAR*)CDebugString(_volinfo.volid) ));
  154. else
  155. {
  156. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set new volid on %c: (%08x)"), VolChar(_iVol), status ));
  157. TrkRaiseNtStatus(status);
  158. }
  159. // Get rid of the log and existing object IDs since we have a new volid.
  160. DeleteAndReinitializeLog();
  161. MarkForMakeAllOidsReborn();
  162. }
  163. //+----------------------------------------------------------------------------
  164. //
  165. // CVolume::VolumeSanityCheck
  166. //
  167. // This routine is called when the volume handles are opened,
  168. // The caller is responsible for calling CLogFile::Initialize and
  169. // CLog::Initialize. The caller must ensure that _volinfo is
  170. // properly loaded prior to the call.
  171. //
  172. //+----------------------------------------------------------------------------
  173. const GUID s_guidInvalidVolId = { /* {d2a2ac27-b89a-11d2-9335-00805ffe11b8} */
  174. 0xd2a2ac27, 0xb89a, 0x11d2,
  175. {0x93, 0x35, 0x00, 0x80, 0x5f, 0xfe, 0x11, 0xb8} };
  176. void
  177. CVolume::VolumeSanityCheck( BOOL fVolIndexSetAlready )
  178. {
  179. NTSTATUS status;
  180. CVolumeId volidVolume;
  181. const CVolumeId volNULL;
  182. const CMachineId mcidLocal( MCID_LOCAL );
  183. TCHAR tszVolumeName[ CCH_MAX_VOLUME_NAME + 1 ];
  184. // Get the volume name that the mount manager has associated with this
  185. // volume.
  186. LONG iVolOld = _iVol;
  187. if( !fVolIndexSetAlready )
  188. {
  189. _iVol = MapVolumeDeviceNameToIndex( _tszVolumeDeviceName );
  190. if( -1 == _iVol )
  191. {
  192. TrkLog(( TRKDBG_VOLUME, TEXT("Volume %s does not appear any longer to have a drive letter (was %c:)"),
  193. _tszVolumeDeviceName, VolChar( iVolOld ) ));
  194. MarkSelfForDelete();
  195. TrkRaiseException( TRK_E_VOLUME_NOT_DRIVE );
  196. }
  197. else if( iVolOld != _iVol )
  198. {
  199. TrkLog(( TRKDBG_VOLUME, TEXT("Volume %c: is now %c:"), VolChar(iVolOld), VolChar(_iVol) ));
  200. }
  201. }
  202. // Get the filesystem-maintained volume ID
  203. TCHAR tszRoot[ MAX_PATH ];
  204. _tcscpy( tszRoot, _tszVolumeDeviceName );
  205. _tcscat( tszRoot, TEXT("\\") );
  206. status = QueryVolumeId(tszRoot, &volidVolume);
  207. g_ptrkwks->_entropy.Put();
  208. if( !NT_SUCCESS(status) && STATUS_OBJECT_NAME_NOT_FOUND != status )
  209. {
  210. // For some reason we couldn't read the NTFS volid
  211. // (e.g. it's been dismounted).
  212. TrkLog(( TRKDBG_VOLUME, TEXT("Couldn't get filesys volid for %c:"), VolChar(_iVol) ));
  213. TrkRaiseNtStatus(status);
  214. }
  215. TrkLog(( TRKDBG_VOLUME, TEXT("VolId (from NTFS) for %c: is %s"),
  216. VolChar(_iVol), (const TCHAR*)CDebugString(volidVolume) ));
  217. // Compare the volume IDs from the filesystem (volume) and from
  218. // the VolInfo structure we keep in the log file. If one is
  219. // set but not the other, then we'll adopt the one that's set.
  220. // If they're both set, but to different values, then we'll
  221. // take the one from NTFS.
  222. if( volNULL == volidVolume && volNULL != _volinfo.volid )
  223. {
  224. // Assume the volid in the VolInfo structure is correct.
  225. // This scenario occurs after a volume is formatted while the service
  226. // is running. In that case, we have the volume info in memory and think
  227. // it's not dirty, but in fact the log file is gone. So, just to be safe,
  228. // we'll go dirty, and the flush at the end will put the latest state out
  229. // to the file.
  230. RaiseIfWriteProtectedVolume();
  231. _fDirty = TRUE;
  232. TrkLog(( TRKDBG_ERROR, TEXT("Duping the volid from the logfile to the volume for %c:"),
  233. VolChar(_iVol) ));
  234. status = SetVolIdOnVolume(_volinfo.volid);
  235. g_ptrkwks->_entropy.Put();
  236. if(!NT_SUCCESS(status))
  237. {
  238. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set a volume ID on %c:"), VolChar(_iVol) ));
  239. TrkRaiseNtStatus(status);
  240. }
  241. SetState( VOL_STATE_NOTOWNED );
  242. }
  243. else if( volidVolume != _volinfo.volid || volNULL == volidVolume)
  244. {
  245. // Either the two volids don't match, or they're both NULL.
  246. if( volNULL != volidVolume && s_guidInvalidVolId != volidVolume )
  247. {
  248. // Assume the volid on the volume (NTFS) is correct.
  249. // If the log has a different volid, it may have invalid move entries.
  250. // So we delete it.
  251. if( volNULL != _volinfo.volid )
  252. DeleteAndReinitializeLog();
  253. _volinfo.Initialize();
  254. // Set _volinfo.volid = volidVolume
  255. SetVolIdInVolInfo( volidVolume );
  256. _volinfo.machine = mcidLocal;
  257. SetState( VOL_STATE_NOTOWNED );
  258. }
  259. else
  260. {
  261. // Both the volume and the _volinfo (the log) are null. We're going to
  262. // go into the not-created state, but first put on a volid so that we
  263. // never have a volume with no ID.
  264. _volinfo.Initialize();
  265. _volinfo.machine = mcidLocal;
  266. // Create a new ID and put it on the volume.
  267. SetLocallyGeneratedVolId(); // Updates _volinfo.volid
  268. // Put ourselves in the not-created state.
  269. TrkAssert( VOL_STATE_OWNED == GetState() );
  270. SetState( VOL_STATE_NOTCREATED );
  271. }
  272. }
  273. // If the machine ID in the log isn't the current machine, then go into
  274. // the not-created state so that we'll re-claim the volume.
  275. if( mcidLocal != _volinfo.machine )
  276. SetState( VOL_STATE_NOTOWNED );
  277. // See if this volume duplicates any other on this system.
  278. CVolume *pvolDuplicate = _pVolMgr->IsDuplicateVolId( this, GetVolumeId() );
  279. if( NULL != pvolDuplicate )
  280. {
  281. // This should never happen; there should never be two
  282. // volumes on the same machine with the same ID.
  283. // When this happens on different machines it gets caught during
  284. // CheckSequenceNumbers, but on the same machine this doesn't work,
  285. // because TrkSvr accepts the Claim of both machines.
  286. TrkLog(( TRKDBG_WARNING,
  287. TEXT("Volume %c: and %c: have duplicate volume IDs. Resetting %c:"),
  288. VolChar(GetIndex()),
  289. VolChar(pvolDuplicate->GetIndex()),
  290. VolChar(GetIndex()) ));
  291. TrkReportEvent( EVENT_TRK_SERVICE_DUPLICATE_VOLIDS, EVENTLOG_ERROR_TYPE,
  292. static_cast<const TCHAR*>(CStringize( VolChar(GetIndex()))),
  293. static_cast<const TCHAR*>(CStringize( VolChar(pvolDuplicate->GetIndex()) )),
  294. TRKREPORT_LAST_PARAM );
  295. SetLocallyGeneratedVolId(); // Updates _volinfo.volid
  296. SetState( CVolume::VOL_STATE_NOTCREATED );
  297. pvolDuplicate->SetState( CVolume::VOL_STATE_NOTOWNED );
  298. pvolDuplicate->Release();
  299. }
  300. // If anything's dirty, flush it now. In the normal initialization path,
  301. // this will have no effect.
  302. Flush();
  303. }
  304. //+----------------------------------------------------------------------------
  305. //
  306. // CVolume::Refresh
  307. //
  308. //+----------------------------------------------------------------------------
  309. void
  310. CVolume::Refresh()
  311. {
  312. HANDLE hVolume = NULL;
  313. NTSTATUS status = STATUS_SUCCESS;
  314. Lock();
  315. __try
  316. {
  317. status = OpenVolume( _tszVolumeDeviceName, &hVolume );
  318. if( NT_SUCCESS(status) )
  319. _iVol = MapVolumeDeviceNameToIndex( _tszVolumeDeviceName );
  320. else if( !IsErrorDueToLockedVolume(status) )
  321. _iVol = -1;
  322. if( -1 == _iVol )
  323. {
  324. TrkLog(( TRKDBG_VOLUME, TEXT("Drive not found in CVolume::Refresh") ));
  325. MarkSelfForDelete();
  326. }
  327. }
  328. __except( BreakOnDebuggableException() )
  329. {
  330. }
  331. if( NULL != hVolume )
  332. NtClose( hVolume );
  333. Unlock();
  334. } // CVolume::Refresh
  335. //+----------------------------------------------------------------------------
  336. //
  337. // CVolume::MarkSelfForDelete
  338. //
  339. // Mark this CVolume to be deleted (not the volume, but the class). The
  340. // delete will actually occur when this object is completely released
  341. // and unlocked. We do, however, as part of this method remove ourself
  342. // from the volume manager's list.
  343. //
  344. //+----------------------------------------------------------------------------
  345. void
  346. CVolume::MarkSelfForDelete()
  347. {
  348. AssertLocked();
  349. if( !_fDeleteSelfPending )
  350. {
  351. TrkLog(( TRKDBG_VOLUME, TEXT("Marking %c: for delete"), VolChar(_iVol) ));
  352. // Show that we need to be deleted. We can't actually delete now, because there
  353. // may be threads active in the object.
  354. _fDeleteSelfPending = TRUE;
  355. // On the final UnLock, Release will be called to counter this AddRef and
  356. // cause the actual delete.
  357. AddRef();
  358. // Take this object out of the Volume Manager's linked list (which will do
  359. // a Release, thus the need for the above AddRef);
  360. _pVolMgr->RemoveVolumeFromLinkedList( this );
  361. }
  362. else
  363. TrkLog(( TRKDBG_VOLUME, TEXT("%c: is already marked for delete"), VolChar(_iVol) ));
  364. }
  365. //+----------------------------------------------------------------------------
  366. //
  367. // CVolume::RegisterPnpVolumeNotification
  368. //
  369. // Register to receive PNP notifications for this volume. If already
  370. // registered, register again (since the volume handle against which
  371. // we'd previously registered may no longer exist).
  372. //
  373. //+----------------------------------------------------------------------------
  374. void
  375. CVolume::RegisterPnPVolumeNotification()
  376. {
  377. DEV_BROADCAST_HANDLE dbchFilter;
  378. HDEVNOTIFY hdnVolumeLock = _hdnVolumeLock;
  379. dbchFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  380. dbchFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
  381. dbchFilter.dbch_handle = _ObjIdIndexChangeNotifier._hDir; // _hVolume;
  382. __try
  383. {
  384. // Register against the oid index handle (as a representative of
  385. // the volume).
  386. hdnVolumeLock = RegisterDeviceNotification((HANDLE)_ssh,
  387. &dbchFilter,
  388. DEVICE_NOTIFY_SERVICE_HANDLE);
  389. if(hdnVolumeLock == NULL)
  390. {
  391. TrkLog((TRKDBG_VOLUME, TEXT("Can't register for volume notifications, %08x"), GetLastError()));
  392. TrkRaiseLastError();
  393. }
  394. // Get rid of our old registration, if we had one.
  395. UnregisterPnPVolumeNotification();
  396. // Keep the new registration.
  397. _hdnVolumeLock = hdnVolumeLock;
  398. TrkLog(( TRKDBG_VOLUME, TEXT("Registered for volume lock/unlock notification on %c: (%p)"),
  399. VolChar(_iVol), _hdnVolumeLock ));
  400. }
  401. __except(BreakOnDebuggableException())
  402. {
  403. TrkLog((TRKDBG_VOLUME, TEXT("Can't register for volume notification, %08x"), GetExceptionCode()));
  404. }
  405. }
  406. //+----------------------------------------------------------------------------
  407. //
  408. // CVolume::UnregisterPnpVolumeNotification
  409. //
  410. // Unregister the device notification handle for this volume (if we have
  411. // one).
  412. //
  413. //+----------------------------------------------------------------------------
  414. void
  415. CVolume::UnregisterPnPVolumeNotification()
  416. {
  417. if(_hdnVolumeLock)
  418. {
  419. if( !UnregisterDeviceNotification(_hdnVolumeLock)) {
  420. TrkLog(( TRKDBG_ERROR, TEXT("UnregisterDeviceNotification failed: %lu"), GetLastError() ));
  421. }
  422. TrkLog(( TRKDBG_VOLUME, TEXT("Unregistered for volume lock/unlock notification on %c: (%p)"),
  423. VolChar(_iVol), _hdnVolumeLock ));
  424. _hdnVolumeLock = NULL;
  425. }
  426. }
  427. //+----------------------------------------------------------------------------
  428. //
  429. // CVolume::DeleteAndReinitializeLog
  430. //
  431. // Delete the volume log and reinitialize it.
  432. //
  433. //+----------------------------------------------------------------------------
  434. void
  435. CVolume::DeleteAndReinitializeLog()
  436. {
  437. // Delete and reinitialize the log
  438. __try
  439. {
  440. RaiseIfWriteProtectedVolume();
  441. TrkLog(( TRKDBG_VOLUME, TEXT("DeleteAndReinitializeLog (%s)"),
  442. _tszVolumeDeviceName ));
  443. if( IsHandsOffVolumeMode() )
  444. // Volume is locked
  445. TrkRaiseNtStatus( STATUS_ACCESS_DENIED );
  446. // Delete the log
  447. _cLogFile.Delete();
  448. // Reinitialize the log file, then the log itself.
  449. _cLogFile.Initialize( NULL, NULL, NULL, VolChar(_iVol) );
  450. _cLog.Initialize( _pLogCallback, _pTrkWksConfiguration, &_cLogFile );
  451. }
  452. __except( IsErrorDueToLockedVolume( GetExceptionCode() )
  453. ? EXCEPTION_EXECUTE_HANDLER
  454. : EXCEPTION_CONTINUE_SEARCH )
  455. {
  456. // If the volume is locked, start the reopen timer and abort.
  457. CloseVolumeHandles(); // Never raises
  458. g_ptrkwks->SetReopenVolumeHandlesTimer();
  459. TrkRaiseException( GetExceptionCode() );
  460. }
  461. }
  462. //+----------------------------------------------------------------------------
  463. //
  464. // CVolume::DeleteLogAndReInitializeVolume
  465. //
  466. // Delete and reinit the log, then reinitialize the rest of the volume.
  467. //
  468. //+----------------------------------------------------------------------------
  469. void
  470. CVolume::DeleteLogAndReInitializeVolume()
  471. {
  472. AssertLocked();
  473. // There's the remote possibility that the VolumeSanityCheck
  474. // call below will call this routine. Just to be paranoid, we
  475. // add protection against an infinite recursion.
  476. if( _fDeleteLogAndReInitializeVolume )
  477. TrkRaiseWin32Error( ERROR_OPEN_FAILED );
  478. _fDeleteLogAndReInitializeVolume = TRUE;
  479. __try
  480. {
  481. TrkLog(( TRKDBG_VOLUME, TEXT("Re-initializing volume %c:"), VolChar(_iVol) ));
  482. // Re-initialize the CLogFile.
  483. DeleteAndReinitializeLog();
  484. // Recreate the volinfo in the new log's header
  485. _fDirty = TRUE; // Force a flush
  486. VolumeSanityCheck();
  487. }
  488. __finally
  489. {
  490. _fDeleteLogAndReInitializeVolume = FALSE;
  491. }
  492. }
  493. //+----------------------------------------------------------------------------
  494. //
  495. // CVolume::UnInitialize
  496. //
  497. // Unregister our PNP handle, free critical sections, etc.
  498. //
  499. //+----------------------------------------------------------------------------
  500. void
  501. CVolume::UnInitialize()
  502. {
  503. if( _fInitialized )
  504. {
  505. IFDBG( _cLocks++; )
  506. UnregisterPnPVolumeNotification();
  507. _ssh = NULL;
  508. if (_hVolume != NULL)
  509. NtClose(_hVolume);
  510. __try
  511. {
  512. _cLogFile.UnInitialize();
  513. }
  514. __except( EXCEPTION_EXECUTE_HANDLER ) // BreakOnDebuggableException() )
  515. {
  516. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolume::UnInitialize after _cLogFile.UnInitialize for %c: %08x"),
  517. VolChar(_iVol), GetExceptionCode() ));
  518. }
  519. _fInitialized = FALSE;
  520. _csHandles.UnInitialize();
  521. _csVolume.UnInitialize();
  522. IFDBG( _cLocks--; )
  523. TrkAssert( 0 == _cLocks );
  524. }
  525. _ObjIdIndexChangeNotifier.UnInitialize();
  526. }
  527. //+----------------------------------------------------------------------------
  528. //
  529. // CVolume::Flush
  530. //
  531. // Flush the volinfo structure, the log, and the logfile. In the process,
  532. // mark the logfile header to show a proper shutdown. If we're in the middle
  533. // of a service shutdown, and there's a problem with the log, don't run
  534. // the recovery code.
  535. //
  536. //+----------------------------------------------------------------------------
  537. void
  538. CVolume::Flush(BOOL fServiceShutdown)
  539. {
  540. Lock();
  541. __try
  542. {
  543. if( _fDirty )
  544. SaveVolInfo();
  545. __try
  546. {
  547. _cLog.Flush( ); // Flushes to CLogFile
  548. _cLogFile.SetShutdown( TRUE ); // Causes a flush to disk if necessary
  549. }
  550. __except( !fServiceShutdown
  551. &&
  552. 0 == _cHandleLocks
  553. &&
  554. IsRecoverableDiskError( GetExceptionCode() )
  555. ? EXCEPTION_EXECUTE_HANDLER
  556. : EXCEPTION_CONTINUE_SEARCH )
  557. {
  558. // Note that we don't handle this exception if the _cHandleLocks is non-zero.
  559. // In this case we're in fast-path and must complete quickly, and the following
  560. // calls could be too time consuming. We must complete quickly because
  561. // CloseVolumeHandles uses that lock, and that call might be called on the
  562. // SCM thread (for e.g. a volume lock event). The SCM thread is shared by
  563. // all services in the process, so we must fast-path anything on it.
  564. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  565. {
  566. CloseAndReopenVolumeHandles(); // Reopen might fail
  567. _cLog.Flush();
  568. _cLogFile.SetShutdown( TRUE );
  569. }
  570. else
  571. {
  572. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  573. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  574. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  575. NULL );
  576. DeleteLogAndReInitializeVolume();
  577. }
  578. }
  579. }
  580. __finally
  581. {
  582. Unlock();
  583. }
  584. }
  585. //+----------------------------------------------------------------------------
  586. //
  587. // CVolume::OpenFile
  588. //
  589. // Open a file on this volume, given the file's object ID.
  590. //
  591. //+----------------------------------------------------------------------------
  592. BOOL
  593. CVolume::OpenFile( const CObjId &objid,
  594. ACCESS_MASK AccessMask,
  595. ULONG ShareAccess,
  596. HANDLE *ph)
  597. {
  598. NTSTATUS status;
  599. Lock();
  600. __try
  601. {
  602. status = OpenFileById( _tszVolumeDeviceName, objid, AccessMask, ShareAccess, 0, ph );
  603. if( NT_SUCCESS(status) )
  604. return TRUE;
  605. else if( STATUS_OBJECT_NAME_NOT_FOUND == status )
  606. return FALSE;
  607. else
  608. TrkRaiseNtStatus( status );
  609. }
  610. __finally
  611. {
  612. Unlock();
  613. }
  614. return( FALSE );
  615. } // CVolume::OpenFile()
  616. //+----------------------------------------------------------------------------
  617. //
  618. // CVolume::LoadSyncVolume
  619. //
  620. // Load the TRKSVR_SYNC_VOLUME message request for this volume, if necessary.
  621. // The call of this message to trksvr is actually sent by the caller. On
  622. // return of that request, UnloadSyncVolume method will be called.
  623. //
  624. //+----------------------------------------------------------------------------
  625. BOOL
  626. CVolume::LoadSyncVolume( TRKSVR_SYNC_VOLUME *pSyncVolume, EAggressiveness eAggressiveness, BOOL* pfSyncNeeded )
  627. {
  628. CVOL_STATE state = GetState();
  629. BOOL fSuccess = FALSE;
  630. Lock();
  631. __try
  632. {
  633. if( !_fVolInfoInitialized )
  634. TrkRaiseException( E_UNEXPECTED );
  635. memset( pSyncVolume, 0, sizeof(*pSyncVolume) );
  636. if(pfSyncNeeded)
  637. {
  638. *pfSyncNeeded = FALSE;
  639. }
  640. // See if it's time to transition from not-owned to not-created.
  641. if( NotOwnedExpired() )
  642. SetState( state = VOL_STATE_NOTCREATED );
  643. // Load the message request, if necessary, based on our current state.
  644. if(state == VOL_STATE_NOTCREATED)
  645. {
  646. // Ordinarily, if we were unable to create this volume due to volume
  647. // quota, we won't try again. But if we're told to be aggressive,
  648. // we'll try anyway.
  649. if( PASSIVE == eAggressiveness && _VolQuotaReached.IsSet() )
  650. {
  651. TrkLog(( TRKDBG_VOLUME, TEXT("Not attempting to create new volume ID on %c:; quota reached"),
  652. VolChar(_iVol) ));
  653. }
  654. else
  655. {
  656. // Generate a new secret for authentication of this volume.
  657. g_ptrkwks->_entropy.Put();
  658. if( !g_ptrkwks->_entropy.InitializeSecret( & _tempSecret ) )
  659. {
  660. // This should never happen - even if there hasn't been enough
  661. // entropy yet, more will be generated.
  662. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't generate secret for volume %c:"), VolChar(_iVol) ));
  663. goto Exit;
  664. }
  665. TrkLog((TRKDBG_VOLUME, TEXT("Generated secret %s for volume %c"),
  666. (const TCHAR*)CDebugString(_tempSecret), VolChar( _iVol )));
  667. // Put the secret in the request, and set the request type to "create"
  668. pSyncVolume->secret = _tempSecret;
  669. pSyncVolume->SyncType = CREATE_VOLUME;
  670. // Show that we put data into the request that should be sent
  671. // to trksvr.
  672. if (pfSyncNeeded != NULL)
  673. *pfSyncNeeded = TRUE;
  674. }
  675. } // case CREATE_VOLUME
  676. else if(state == VOL_STATE_NOTOWNED)
  677. {
  678. // Attempt to claim this volume.
  679. pSyncVolume->volume = _volinfo.volid;
  680. pSyncVolume->secretOld = _volinfo.secret;
  681. pSyncVolume->SyncType = CLAIM_VOLUME;
  682. // Generate a new secret.
  683. if( !g_ptrkwks->_entropy.InitializeSecret( &_tempSecret ) )
  684. {
  685. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't generate secret for volume %c:"), VolChar(_iVol) ));
  686. goto Exit;
  687. }
  688. TrkLog((TRKDBG_VOLUME, TEXT("Generated secret %s for volume %c"),
  689. (const TCHAR*)CDebugString(_tempSecret), VolChar( _iVol )));
  690. pSyncVolume->secret = _tempSecret;
  691. // Show that the request should be sent.
  692. if (pfSyncNeeded != NULL)
  693. *pfSyncNeeded = TRUE;
  694. } // case CLAIM_VOLUME
  695. }
  696. __except( BreakOnDebuggableException() )
  697. {
  698. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in LoadSyncVolume for %c: %08x"),
  699. VolChar(_iVol), GetExceptionCode() ));
  700. goto Exit;
  701. }
  702. fSuccess = TRUE;
  703. Exit:
  704. Unlock();
  705. return( fSuccess );
  706. }
  707. //+----------------------------------------------------------------------------
  708. //
  709. // CVolume::OnRestore
  710. //
  711. // Not currently implemented.
  712. //
  713. //+----------------------------------------------------------------------------
  714. HRESULT
  715. CVolume::OnRestore()
  716. {
  717. return( E_NOTIMPL );
  718. #if 0
  719. HRESULT hr = E_FAIL;
  720. CVolumeId volidVolume;
  721. const CMachineId mcidLocal( MCID_LOCAL );
  722. NTSTATUS status;
  723. memset( &_volinfo, 0, sizeof(_volinfo) );
  724. hr = S_OK;
  725. Lock();
  726. __try // __finally
  727. {
  728. __try
  729. {
  730. // Get volume id from two different places: in the log file, and on
  731. // the volume. If the two
  732. // disagree, use the object id in the log file, overwrite the other
  733. // one. Put the volume into NOTOWNED state.
  734. TrkLog(( TRKDBG_VOLUME,
  735. TEXT("Checking for recorded id's on volume %c:"),
  736. VolChar(_iVol) ));
  737. LoadVolInfo();
  738. TrkLog(( TRKDBG_VOLUME, TEXT("volume id in log file ---- (%s) %c:"),
  739. CDebugString(_volinfo.volid)._tsz, VolChar(_iVol) ));
  740. status = QueryVolumeId(_tszVolumeDeviceName, &volidVolume);
  741. if(!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND)
  742. {
  743. TrkLog((TRKDBG_ERROR, TEXT("Can't get id from volume %c"), VolChar(_iVol)));
  744. SetState(VOL_STATE_NOTCREATED);
  745. }
  746. // if no id is set on the volume, adopt from the log file
  747. if(volidVolume == CVolumeId() && _volinfo.volid != CVolumeId())
  748. {
  749. status = SetVolIdOnVolume(_volinfo.volid);
  750. g_ptrkwks->_entropy.Put();
  751. if(!NT_SUCCESS(status))
  752. {
  753. TrkRaiseNtStatus(status);
  754. }
  755. SetState( VOL_STATE_NOTOWNED );
  756. }
  757. else if(volidVolume != _volinfo.volid)
  758. // The log file could have been copied or moved before the restore
  759. // happened, in order to be safe we trash the volume.
  760. {
  761. SetState(VOL_STATE_NOTCREATED);
  762. }
  763. hr = S_OK;
  764. }
  765. __except (BreakOnDebuggableException())
  766. {
  767. TrkLog((TRKDBG_ERROR, TEXT("OnRestore failed")));
  768. hr = GetExceptionCode();
  769. }
  770. // If an un-recoverable log error was raised, re-initialize everything
  771. if( TRK_E_CORRUPT_LOG == hr )
  772. {
  773. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  774. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  775. NULL );
  776. __try
  777. {
  778. DeleteLogAndReInitializeVolume();
  779. }
  780. __except( BreakOnDebuggableException())
  781. {
  782. hr = GetExceptionCode();
  783. }
  784. }
  785. else if( IsErrorDueToLockedVolume(hr) )
  786. {
  787. CloseAndReopenVolumeHandles(); // Reopen might fail
  788. TrkRaiseException( hr );
  789. }
  790. }
  791. __finally
  792. {
  793. Unlock();
  794. }
  795. return hr;
  796. #endif // #if 0
  797. }
  798. //+----------------------------------------------------------------------------
  799. //
  800. // CVolume::LoadQueryVolume
  801. //
  802. // Load a TRKSVR_SYNC_VOLUME request for this volume, if necessary. If we
  803. // load it, the caller will send it to trksvr. On return of that request,
  804. // UnloadQueryVolume method will be called.
  805. //
  806. //+----------------------------------------------------------------------------
  807. BOOL
  808. CVolume::LoadQueryVolume( TRKSVR_SYNC_VOLUME *pQueryVolume )
  809. {
  810. BOOL fSuccess = FALSE;
  811. Lock();
  812. __try
  813. {
  814. // Don't do anything we're not even in trksvr.
  815. if(GetState() == VOL_STATE_NOTCREATED)
  816. {
  817. goto Exit;
  818. }
  819. // Put our volid & log sequence number into the request.
  820. memset( pQueryVolume, 0, sizeof(*pQueryVolume) );
  821. pQueryVolume->SyncType = QUERY_VOLUME;
  822. pQueryVolume->volume = _volinfo.volid;
  823. pQueryVolume->seq = _cLog.GetNextSeqNumber(); // Never raises
  824. }
  825. __except( BreakOnDebuggableException() )
  826. {
  827. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in LoadQueryVolume for %c: %08x"),
  828. VolChar(_iVol), GetExceptionCode() ));
  829. goto Exit;
  830. }
  831. fSuccess = TRUE;
  832. Exit:
  833. Unlock();
  834. return( fSuccess );
  835. }
  836. // Originally, trkwks bundle up volume requests (sync, claim, create) and call the DC. After the
  837. // DC returns, this function is called to put necessary information back on the volume. Now the
  838. // DC callback mechanism is added. When there are create volume requests, the DC will callback and
  839. // this function is called by the DC callback function. The DC needs to know if each create volume
  840. // request is successfully finished by the trkwks, so this function has to put an HRESULT to
  841. // indicate that in the hr field of the TRKSVR_SYNC_VOLUME structure.
  842. //+----------------------------------------------------------------------------
  843. //
  844. // CVolume::UnloadSyncVolume
  845. //
  846. // A sync-volume request was loaded in LoadSyncVolume, sent to trksvr, and
  847. // we now need to interpret the result. If we successfully completed
  848. // a create or claim, we'll go into the owned state.
  849. //
  850. //+----------------------------------------------------------------------------
  851. BOOL
  852. CVolume::UnloadSyncVolume( TRKSVR_SYNC_VOLUME *pSyncVolume )
  853. {
  854. BOOL fSuccess = FALSE;
  855. BOOL fWrite = FALSE;
  856. CMachineId mcidLocal( MCID_LOCAL );
  857. Lock();
  858. __try
  859. {
  860. if( !_fVolInfoInitialized )
  861. TrkRaiseException( E_UNEXPECTED );
  862. if(pSyncVolume->hr == S_OK)
  863. {
  864. // Clear the bit that indicates we've reported a vol quota event.
  865. // That way, the next time we get a volume quota error, we'll report
  866. // to the event log.
  867. _VolQuotaReached.Clear();
  868. switch( pSyncVolume->SyncType )
  869. {
  870. case CREATE_VOLUME:
  871. {
  872. NTSTATUS status = STATUS_SUCCESS;
  873. // Write the volume ID to the volume meta-data.
  874. status = SetVolIdOnVolume( pSyncVolume->volume );
  875. g_ptrkwks->_entropy.Put();
  876. if( !NT_SUCCESS(status) )
  877. __leave;
  878. TrkLog(( TRKDBG_VOLUME, TEXT("Newly-created vol id = %s, %c:"),
  879. (const TCHAR*)CDebugString(pSyncVolume->volume),
  880. VolChar(_iVol) ));
  881. // Create a fresh log
  882. DeleteAndReinitializeLog();
  883. // Update _volinfo
  884. _fDirty = TRUE;
  885. _volinfo.cftLastRefresh = pSyncVolume->ftLastRefresh;
  886. // Set _volinfo.volid = pSyncVolume->volume
  887. SetVolIdInVolInfo( pSyncVolume->volume );
  888. _volinfo.machine = mcidLocal;
  889. _volinfo.secret = _tempSecret;
  890. // And update our state.
  891. SetState( VOL_STATE_OWNED ); // Flushes _volinfo
  892. TrkAssert( VOL_STATE_OWNED == GetState() );
  893. TrkReportEvent( EVENT_TRK_SERVICE_VOLUME_CREATE, EVENTLOG_INFORMATION_TYPE,
  894. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  895. static_cast<const TCHAR*>( CStringize( _volinfo.volid )),
  896. NULL );
  897. } // case CREATE_VOLUME
  898. break;
  899. case CLAIM_VOLUME:
  900. {
  901. RaiseIfWriteProtectedVolume();
  902. _fDirty = TRUE;
  903. _volinfo.machine = mcidLocal;
  904. _volinfo.cftLastRefresh = pSyncVolume->ftLastRefresh;
  905. _volinfo.secret = _tempSecret;
  906. SetState( VOL_STATE_OWNED ); // Flushes _volinfo
  907. TrkAssert( VOL_STATE_OWNED == GetState() );
  908. Seek( pSyncVolume->seq );
  909. TrkReportEvent( EVENT_TRK_SERVICE_VOLUME_CLAIM, EVENTLOG_INFORMATION_TYPE,
  910. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  911. static_cast<const TCHAR*>( CStringize( _volinfo.volid )),
  912. TRKREPORT_LAST_PARAM );
  913. } // case CLAIM_VOLUME
  914. break;
  915. default:
  916. TrkAssert( FALSE && TEXT("Invalid SyncType given to CVolume::Serialize") );
  917. break;
  918. } // switch
  919. fSuccess = TRUE;
  920. } // if(pSyncVolume->hr == S_OK)
  921. else
  922. {
  923. // If this is a quota error, log it (but only log it once
  924. // per machine per transition).
  925. if( TRK_E_VOLUME_QUOTA_EXCEEDED == pSyncVolume->hr )
  926. {
  927. TrkLog(( TRKDBG_ERROR, TEXT("Vol quota reached") ));
  928. if( !_VolQuotaReached.IsSet() )
  929. {
  930. _VolQuotaReached.Set();
  931. TrkReportEvent( EVENT_TRK_SERVICE_VOL_QUOTA_EXCEEDED, EVENTLOG_WARNING_TYPE,
  932. TRKREPORT_LAST_PARAM );
  933. }
  934. // We'll call this success so that we don't retry. We'll try again
  935. // later when the infrequent timer goes off.
  936. fSuccess = TRUE;
  937. }
  938. SetState(VOL_STATE_NOTOWNED);
  939. __leave;
  940. }
  941. }
  942. __except( BreakOnDebuggableException() )
  943. {
  944. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in UnloadSyncVolume for %c: %08x"),
  945. VolChar(_iVol), GetExceptionCode() ));
  946. }
  947. if( !fSuccess && pSyncVolume->SyncType == CREATE_VOLUME )
  948. {
  949. g_ptrkwks->_entropy.ReturnUnusedSecret( & _tempSecret );
  950. _tempSecret = CVolumeSecret();
  951. if( SUCCEEDED(pSyncVolume->hr) )
  952. pSyncVolume->hr = E_FAIL;
  953. }
  954. Unlock();
  955. return( fSuccess );
  956. } // CVolume::UnloadSyncVolume
  957. //+----------------------------------------------------------------------------
  958. //
  959. // CVolume::UnloadQueryVolume
  960. //
  961. // The volume manager called LoadQueryVolume, sent the request to trskvr,
  962. // and is now giving us the result.
  963. //
  964. //+----------------------------------------------------------------------------
  965. BOOL
  966. CVolume::UnloadQueryVolume( const TRKSVR_SYNC_VOLUME *pQueryVolume )
  967. {
  968. BOOL fSuccess = FALSE;
  969. Lock();
  970. __try
  971. {
  972. // Was the request successful?
  973. if(pQueryVolume->hr == S_OK)
  974. {
  975. // Go into the owned state, if we're not there
  976. // already.
  977. SetState( VOL_STATE_OWNED );
  978. // Seek the log to match what trksvr expects. If this causes the
  979. // seek pointer to be backed up, it will set the timer to trigger
  980. // a new move-notification to trksvr.
  981. Seek( pQueryVolume->seq );
  982. }
  983. else // DC didn't return VOLUME_OK
  984. {
  985. TrkLog((TRKDBG_VOLUME, TEXT("DC returned %s for QueryVolume of volume %s (%c:) -> VOL_STATE_NOTOWNED"),
  986. GetErrorString(pQueryVolume->hr),
  987. (const TCHAR*)CDebugString(pQueryVolume->volume),
  988. VolChar(_iVol) ));
  989. // If there was a problem, go into the not-owned state.
  990. SetState(VOL_STATE_NOTOWNED);
  991. }
  992. }
  993. __except( BreakOnDebuggableException() )
  994. {
  995. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in UnloadQueryVolume for %c: %08x"),
  996. VolChar(_iVol), GetExceptionCode() ));
  997. goto Exit;
  998. }
  999. fSuccess = TRUE;
  1000. Exit:
  1001. Unlock();
  1002. return( fSuccess );
  1003. }
  1004. //+----------------------------------------------------------------------------
  1005. //
  1006. // CVolume::Append
  1007. //
  1008. // Append a move notification to the end of this volume's log.
  1009. //
  1010. //+----------------------------------------------------------------------------
  1011. void
  1012. CVolume::Append( const CDomainRelativeObjId &droidCurrent,
  1013. const CDomainRelativeObjId &droidNew,
  1014. const CMachineId &mcidNew,
  1015. const CDomainRelativeObjId &droidBirth)
  1016. {
  1017. //TrkLog((TRKDBG_VOL_REFCNT, TEXT("CVolume(%08x)::Append refcnt=%d (should be 2, sometimes >2)"), this, _lRef));
  1018. Lock();
  1019. __try // __finally
  1020. {
  1021. // Validate the IDs
  1022. const CVolumeId volidZero;
  1023. const CObjId objidZero;
  1024. if( volidZero == droidCurrent.GetVolumeId()
  1025. ||
  1026. objidZero == droidCurrent.GetObjId()
  1027. ||
  1028. volidZero == droidNew.GetVolumeId()
  1029. ||
  1030. objidZero == droidNew.GetObjId()
  1031. ||
  1032. volidZero == droidBirth.GetVolumeId()
  1033. ||
  1034. objidZero == droidBirth.GetObjId() )
  1035. {
  1036. // In the append path, we only raise NTSTATUS errors, not HRESULTs
  1037. TrkRaiseException( STATUS_OBJECT_NAME_INVALID );
  1038. }
  1039. __try // __except
  1040. {
  1041. _cLog.Append( droidCurrent.GetVolumeId(), droidCurrent.GetObjId(), droidNew, mcidNew, droidBirth );
  1042. }
  1043. __except( IsRecoverableDiskError( GetExceptionCode() )
  1044. ? EXCEPTION_EXECUTE_HANDLER
  1045. : EXCEPTION_CONTINUE_SEARCH )
  1046. {
  1047. // We had a potentially recoverable exception. Try to handle it
  1048. // and retry the append.
  1049. if( IsErrorDueToLockedVolume( GetExceptionCode() ) )
  1050. {
  1051. CloseAndReopenVolumeHandles(); // Reopen might fail
  1052. }
  1053. else
  1054. {
  1055. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1056. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1057. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1058. NULL );
  1059. // The log is corrupted. Re-initialize, then attempt the Append again.
  1060. // If this raises, it's an unrecoverable exception, so we just pass
  1061. // it up.
  1062. DeleteLogAndReInitializeVolume();
  1063. }
  1064. // Retry the Append, which could raise again, but this time we won't catch it.
  1065. _cLog.Append( droidCurrent.GetVolumeId(), droidCurrent.GetObjId(), droidNew, mcidNew, droidBirth );
  1066. }
  1067. }
  1068. __finally
  1069. {
  1070. Unlock();
  1071. }
  1072. }
  1073. //+----------------------------------------------------------------------------
  1074. //
  1075. // CVolume::Read
  1076. //
  1077. // Read one or more entries from the log, from the current seek position.
  1078. //
  1079. //+----------------------------------------------------------------------------
  1080. void
  1081. CVolume::Read(CObjId *pobjidCurrent,
  1082. CDomainRelativeObjId *pdroidBirth,
  1083. CDomainRelativeObjId *pdroidNew,
  1084. SequenceNumber *pseqFirst,
  1085. ULONG *pcRead)
  1086. {
  1087. Lock();
  1088. __try // __finally
  1089. {
  1090. __try
  1091. {
  1092. _cLog.Read( pobjidCurrent, pdroidBirth, pdroidNew,
  1093. pseqFirst, pcRead );
  1094. }
  1095. __except( IsRecoverableDiskError( GetExceptionCode() )
  1096. ? EXCEPTION_EXECUTE_HANDLER
  1097. : EXCEPTION_CONTINUE_SEARCH )
  1098. {
  1099. // Try to recover from this error and if possible retry
  1100. // the read.
  1101. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1102. {
  1103. CloseAndReopenVolumeHandles(); // Reopen might fail
  1104. // Retry the read, which could raise again, but this time we won't
  1105. // catch it.
  1106. TrkLog(( TRKDBG_VOLUME, TEXT("Retrying CLog::Read") ));
  1107. _cLog.Read( pobjidCurrent, pdroidBirth, pdroidNew,
  1108. pseqFirst, pcRead );
  1109. }
  1110. else
  1111. {
  1112. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1113. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1114. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1115. NULL );
  1116. // The log is corrupted. Re-initialize, then pass up the error.
  1117. DeleteLogAndReInitializeVolume();
  1118. TrkRaiseException( GetExceptionCode() );
  1119. }
  1120. }
  1121. }
  1122. __finally
  1123. {
  1124. Unlock();
  1125. }
  1126. }
  1127. //+----------------------------------------------------------------------------
  1128. //
  1129. // CVolume::Search
  1130. //
  1131. // Search the log for a move-notification (from droidCurrent).
  1132. //
  1133. //+----------------------------------------------------------------------------
  1134. BOOL
  1135. CVolume::Search( const CDomainRelativeObjId & droidCurrent, CDomainRelativeObjId * pdroidNew,
  1136. CMachineId *pmcidNew, CDomainRelativeObjId * pdroidBirth )
  1137. {
  1138. BOOL fFound = FALSE;
  1139. Lock();
  1140. __try // __finally
  1141. {
  1142. __try
  1143. {
  1144. // Perfbug: Don't hold the log locked during the whole search such that
  1145. // it locks out Appends.
  1146. fFound = _cLog.Search( droidCurrent.GetObjId(), pdroidNew, pmcidNew, pdroidBirth );
  1147. }
  1148. __except( IsRecoverableDiskError( GetExceptionCode() )
  1149. ? EXCEPTION_EXECUTE_HANDLER
  1150. : EXCEPTION_CONTINUE_SEARCH )
  1151. {
  1152. // Try to recover from this error and retry the search.
  1153. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1154. {
  1155. CloseAndReopenVolumeHandles(); // Reopen might fail
  1156. // Retry the search, which could raise again, but this time we won't catch it.
  1157. fFound = _cLog.Search( droidCurrent.GetObjId(), pdroidNew, pmcidNew, pdroidBirth );
  1158. }
  1159. else
  1160. {
  1161. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1162. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1163. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1164. NULL );
  1165. // The log is corrupted. Re-initialize, and pass up the error.
  1166. DeleteLogAndReInitializeVolume();
  1167. TrkRaiseException( GetExceptionCode() );
  1168. }
  1169. }
  1170. }
  1171. __finally
  1172. {
  1173. Unlock();
  1174. }
  1175. return( fFound );
  1176. }
  1177. //+----------------------------------------------------------------------------
  1178. //
  1179. // CVolume::Seek
  1180. //
  1181. // Seek the log to a particular sequence number.
  1182. //
  1183. //+----------------------------------------------------------------------------
  1184. BOOL
  1185. CVolume::Seek( SequenceNumber seq )
  1186. {
  1187. BOOL fSuccess = FALSE;
  1188. Lock();
  1189. __try
  1190. {
  1191. __try
  1192. {
  1193. fSuccess = _cLog.Seek( seq );
  1194. }
  1195. __except( IsRecoverableDiskError( GetExceptionCode() )
  1196. ? EXCEPTION_EXECUTE_HANDLER
  1197. : EXCEPTION_CONTINUE_SEARCH )
  1198. {
  1199. // Try to recover from this error and retry the seek.
  1200. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1201. {
  1202. CloseAndReopenVolumeHandles(); // Reopen might fail
  1203. // Retry the Seek, which could raise again, but this time we won't catch it.
  1204. fSuccess = _cLog.Seek( seq );
  1205. }
  1206. else
  1207. {
  1208. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1209. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1210. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1211. NULL );
  1212. // The log is corrupted. Re-initialize and pass up the error.
  1213. DeleteLogAndReInitializeVolume();
  1214. TrkRaiseException( GetExceptionCode() );
  1215. }
  1216. }
  1217. }
  1218. __finally
  1219. {
  1220. Unlock();
  1221. }
  1222. if( fSuccess )
  1223. TrkLog(( TRKDBG_VOLUME, TEXT("Log on %c: sought to seq %d"),
  1224. VolChar(_iVol), seq ));
  1225. else
  1226. TrkLog(( TRKDBG_VOLUME, TEXT("Log on %c: couldn't be sought to seq %d"),
  1227. VolChar(_iVol), seq ));
  1228. return( fSuccess );
  1229. }
  1230. //+----------------------------------------------------------------------------
  1231. //
  1232. // CVolume::Seek
  1233. //
  1234. // Seek to a relative (e.g. back up 2) or absolute (e.g. first) position.
  1235. //
  1236. //+----------------------------------------------------------------------------
  1237. void
  1238. CVolume::Seek( int origin, int iSeek )
  1239. {
  1240. Lock();
  1241. __try // __finally
  1242. {
  1243. __try
  1244. {
  1245. _cLog.Seek( origin, iSeek );
  1246. }
  1247. __except( IsRecoverableDiskError( GetExceptionCode() )
  1248. ? EXCEPTION_EXECUTE_HANDLER
  1249. : EXCEPTION_CONTINUE_SEARCH )
  1250. {
  1251. // Attempt to recover from this error and retry the seek.
  1252. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1253. {
  1254. CloseAndReopenVolumeHandles(); // Reopen might fail
  1255. // Retry the Seek, which could raise again, but this time we won't catch it.
  1256. _cLog.Seek( origin, iSeek );
  1257. }
  1258. else
  1259. {
  1260. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1261. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1262. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1263. NULL );
  1264. // The log is corrupted. Re-initialize and pass up the error.
  1265. DeleteLogAndReInitializeVolume();
  1266. TrkRaiseException( GetExceptionCode() );
  1267. }
  1268. }
  1269. }
  1270. __finally
  1271. {
  1272. Unlock();
  1273. }
  1274. }
  1275. //+----------------------------------------------------------------------------
  1276. //
  1277. // CVolume::GetVolumeId
  1278. //
  1279. // Get the volume ID without taking the lock. This was done
  1280. // so that CVolumeManager::IsDuplicateID can check the volid
  1281. // of other volumes without deadlocking. Otherwise we run the
  1282. // risk of one volume holding its locks and trying to get
  1283. // another volume's lock (using GetVolumeId on that volume)
  1284. // while another thread is in that volume doing the same for
  1285. // this volume.
  1286. //
  1287. //+----------------------------------------------------------------------------
  1288. const CVolumeId
  1289. CVolume::GetVolumeId()
  1290. {
  1291. CVolumeId volid;
  1292. ULONG cAttempts = 0;
  1293. // Spin until we get a good volid.
  1294. while( TRUE )
  1295. {
  1296. // Get the update counter before and after reading
  1297. // from the volid. (This assumes that
  1298. // reading the long is atomic.)
  1299. LONG lVolidUpdatesBefore = _lVolidUpdates;
  1300. volid = _volinfo.volid;
  1301. LONG lVolidUpdatesAfter = _lVolidUpdates;
  1302. // When the _volinfo is updated, the _lVolidUpdates is
  1303. // incremented before and after the update. So if there
  1304. // was an update in progress when we started, it will
  1305. // be an odd number.
  1306. //
  1307. // Ensure there was no update in progress when we read
  1308. // the volid, and there was no update started while we
  1309. // were reading the volid.
  1310. if( (lVolidUpdatesBefore & 1)
  1311. ||
  1312. lVolidUpdatesBefore != lVolidUpdatesAfter )
  1313. {
  1314. // Check for timeout (30 seconds)
  1315. if( 3000 < ++cAttempts )
  1316. {
  1317. TrkLog(( TRKDBG_ERROR, TEXT("Failed spin in GetVolumeId") ));
  1318. TrkRaiseWin32Error( WAIT_TIMEOUT );
  1319. }
  1320. // Wait for the update to complete then try again.
  1321. Sleep( 10 );
  1322. continue;
  1323. }
  1324. else
  1325. break;
  1326. }
  1327. return( volid );
  1328. }
  1329. //+----------------------------------------------------------------------------
  1330. //
  1331. // CVolume::GetState
  1332. //
  1333. // Get the current state of this volume (owned, not-owned, or not-created).
  1334. //
  1335. //+----------------------------------------------------------------------------
  1336. CVolume::CVOL_STATE
  1337. CVolume::GetState()
  1338. {
  1339. CVolumeId volNULL;
  1340. CVOL_STATE state = VOL_STATE_UNKNOWN;
  1341. Lock();
  1342. __try
  1343. {
  1344. if( _volinfo.fNotCreated )
  1345. {
  1346. state = VOL_STATE_NOTCREATED;
  1347. }
  1348. // If the time we entered the not-owned state is non-zero, then
  1349. // we're certainly in the not-owned state. Also, if the machine ID
  1350. // in the _volinfo header doesn't match the local machine, we're
  1351. // not owned.
  1352. else if(_volinfo.cftEnterNotOwned != 0
  1353. ||
  1354. _volinfo.machine != CMachineId(MCID_LOCAL)
  1355. )
  1356. {
  1357. // Is this the first time that we realized we're not owned?
  1358. if( !IsWriteProtectedVolume() && 0 == _volinfo.cftEnterNotOwned )
  1359. {
  1360. _volinfo.cftEnterNotOwned.SetToUTC();
  1361. _fDirty = TRUE;
  1362. Flush();
  1363. }
  1364. state = VOL_STATE_NOTOWNED;
  1365. }
  1366. // Otherwise, we must be properly owned.
  1367. else
  1368. {
  1369. state = VOL_STATE_OWNED;
  1370. }
  1371. }
  1372. __finally
  1373. {
  1374. Unlock();
  1375. }
  1376. return state;
  1377. }
  1378. //+----------------------------------------------------------------------------
  1379. //
  1380. // CVolume::SetState
  1381. //
  1382. // Change the current state of the volume. This checks for valid transitions.
  1383. // For example, you can't transition from not-created to not-owned (such
  1384. // a request is silently ignored). This alleviates the caller from having
  1385. // to perform this logic.
  1386. //
  1387. //+----------------------------------------------------------------------------
  1388. void
  1389. CVolume::SetState(CVOL_STATE volstateTarget)
  1390. {
  1391. CVOL_STATE volstateCurrent = GetState();
  1392. NTSTATUS status = STATUS_SUCCESS;
  1393. VolumePersistentInfo volinfoNew = _volinfo;
  1394. BOOL fDirtyNew = _fDirty;
  1395. // Make sure the volume is writeable
  1396. RaiseIfWriteProtectedVolume();
  1397. Lock();
  1398. __try
  1399. {
  1400. switch( volstateTarget )
  1401. {
  1402. case VOL_STATE_NOTOWNED:
  1403. // We can only go to not-owned from owned.
  1404. if( VOL_STATE_OWNED == volstateCurrent )
  1405. {
  1406. TrkAssert( !volinfoNew.fNotCreated );
  1407. TrkLog(( TRKDBG_VOLUME, TEXT("Entering not-owned state on vol %c:"), VolChar(_iVol) ));
  1408. RaiseIfWriteProtectedVolume();
  1409. fDirtyNew = TRUE;
  1410. volinfoNew.cftEnterNotOwned.SetToUTC();
  1411. }
  1412. break;
  1413. case VOL_STATE_NOTCREATED:
  1414. // We can always go to not-created.
  1415. if( volstateCurrent != VOL_STATE_NOTCREATED )
  1416. {
  1417. TrkLog(( TRKDBG_VOLUME, TEXT("Entering not-created state on vol %c:"), VolChar(_iVol) ));
  1418. RaiseIfWriteProtectedVolume();
  1419. fDirtyNew = TRUE;
  1420. volinfoNew.fNotCreated = TRUE;
  1421. volinfoNew.cftEnterNotOwned = CFILETIME(0);
  1422. }
  1423. break;
  1424. case VOL_STATE_OWNED:
  1425. if( VOL_STATE_NOTCREATED == volstateCurrent )
  1426. {
  1427. // We're going from not-created to owned, so we need to make
  1428. // all our OIDs reborn.
  1429. TrkLog(( TRKDBG_VOLUME, TEXT("Entering owned state (from not-created) on vol %c:"), VolChar(_iVol) ));
  1430. RaiseIfWriteProtectedVolume();
  1431. fDirtyNew = TRUE;
  1432. volinfoNew.fNotCreated = FALSE;
  1433. TrkAssert( CFILETIME(0) == volinfoNew.cftEnterNotOwned );
  1434. // Since we now have a new volid, we must give all the existing
  1435. // files new object IDs.
  1436. //MarkForMakeAllOidsReborn();
  1437. volinfoNew.fDoMakeAllOidsReborn = TRUE;
  1438. }
  1439. else if( VOL_STATE_NOTOWNED == volstateCurrent )
  1440. {
  1441. // We're going from not-owned to owned.
  1442. TrkLog(( TRKDBG_VOLUME, TEXT("Entering owned state (from not-owned) on vol %c:"), VolChar(_iVol) ));
  1443. TrkAssert( !volinfoNew.fNotCreated );
  1444. RaiseIfWriteProtectedVolume();
  1445. fDirtyNew = TRUE;
  1446. volinfoNew.cftEnterNotOwned = CFILETIME(0);
  1447. }
  1448. TrkAssert( CVolumeId() != _volinfo.volid );
  1449. break;
  1450. default:
  1451. TrkAssert( !TEXT("Bad target state in CVolume::SetState") );
  1452. } // switch( volstateTarget )
  1453. // If we modified the volinfo, write it back out.
  1454. _volinfo = volinfoNew;
  1455. _fDirty |= fDirtyNew;
  1456. Flush();
  1457. }
  1458. __finally
  1459. {
  1460. Unlock();
  1461. }
  1462. return;
  1463. }
  1464. //+----------------------------------------------------------------------------
  1465. //
  1466. // CVolume::NotOwnedExpired
  1467. //
  1468. // Have we been in the not-owned state for long enough that we should be
  1469. // in the not-created state?
  1470. //
  1471. //+----------------------------------------------------------------------------
  1472. BOOL
  1473. CVolume::NotOwnedExpired()
  1474. {
  1475. Lock();
  1476. __try
  1477. {
  1478. if(_volinfo.cftEnterNotOwned != 0)
  1479. {
  1480. CFILETIME cftDiff = CFILETIME() - _volinfo.cftEnterNotOwned;
  1481. ULONG SecondsDiff = static_cast<ULONG>((LONGLONG)cftDiff/10000000);
  1482. if(SecondsDiff > _pTrkWksConfiguration->GetVolNotOwnedExpireLimit())
  1483. {
  1484. return TRUE;
  1485. }
  1486. }
  1487. }
  1488. __finally
  1489. {
  1490. Unlock();
  1491. }
  1492. return FALSE;
  1493. }
  1494. //+----------------------------------------------------------------------------
  1495. //
  1496. // CVolume::MakeAllOidsReborn
  1497. //
  1498. // Reset (zero out) the birth IDs (actually, all 48 extended bytes) of
  1499. // all the files on this volume. That makes the file no longer a link source,
  1500. // so we won't try to track it. If someone subsequently makes a link
  1501. // to it, NTFS will fill in a new birth ID.
  1502. //
  1503. //+----------------------------------------------------------------------------
  1504. BOOL
  1505. CVolume::MakeAllOidsReborn()
  1506. {
  1507. CObjIdEnumerator oie;
  1508. BOOL fSuccess = FALSE;
  1509. CObjId objid;
  1510. CDomainRelativeObjId droidBirth;
  1511. NTSTATUS status;
  1512. CVolumeId vidNull;
  1513. BOOL fLocked = FALSE;
  1514. __try
  1515. {
  1516. // Give all the files with object IDs a fresh birth ID, as if the
  1517. // file had first been linked to on this volume.
  1518. TrkLog(( TRKDBG_VOLUME, TEXT("Making OIDs reborn on volume %c:"), VolChar(_iVol) ));
  1519. if(oie.Initialize(_tszVolumeDeviceName))
  1520. {
  1521. if(oie.FindFirst(&objid, &droidBirth))
  1522. {
  1523. do
  1524. {
  1525. g_ptrkwks->RaiseIfStopped();
  1526. // If this has what looks like an invalid birth ID, ignore it.
  1527. if( CObjId() == droidBirth.GetObjId() )
  1528. continue;
  1529. // We only take the lock directly around the make-reborn
  1530. // call, since with the sleep below we could be in this routine
  1531. // for a while.
  1532. Lock(); fLocked = TRUE;
  1533. TrkAssert( 1 == _cLocks );
  1534. MakeObjIdReborn( _tszVolumeDeviceName, objid );
  1535. Unlock(); fLocked = FALSE;
  1536. Sleep( 100 ); // don't hog the machine
  1537. } while(oie.FindNext(&objid, &droidBirth));
  1538. }
  1539. }
  1540. }
  1541. __except( BreakOnDebuggableException() )
  1542. {
  1543. __try
  1544. {
  1545. if( TRK_E_CORRUPT_LOG == GetExceptionCode() )
  1546. {
  1547. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1548. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1549. NULL );
  1550. BreakIfRequested();
  1551. DeleteLogAndReInitializeVolume();
  1552. }
  1553. else if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1554. {
  1555. CloseAndReopenVolumeHandles();
  1556. TrkRaiseException( GetExceptionCode() );
  1557. }
  1558. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolume::MakeAllOidsReborn for %c: %08x"),
  1559. VolChar(_iVol), GetExceptionCode() ));
  1560. }
  1561. __finally
  1562. {
  1563. if( fLocked )
  1564. Unlock();
  1565. }
  1566. goto Exit;
  1567. }
  1568. fSuccess = TRUE;
  1569. Exit:
  1570. oie.UnInitialize();
  1571. if(!fSuccess)
  1572. {
  1573. TrkLog((TRKDBG_ERROR,
  1574. TEXT("Can't delete all object ids on volume %c:"),
  1575. VolChar(_iVol) ));
  1576. }
  1577. return fSuccess;
  1578. }
  1579. //+----------------------------------------------------------------------------
  1580. //
  1581. // CVolume::OnHandlesMustClose
  1582. //
  1583. // This routine is called from CLogFile if it discovers that the log file
  1584. // needs to be closed (an oplock break). We close all handles on the volume
  1585. // and start the reopen timer.
  1586. //
  1587. //+----------------------------------------------------------------------------
  1588. void
  1589. CVolume::OnHandlesMustClose()
  1590. {
  1591. CloseVolumeHandles(); // Doesn't raise
  1592. g_ptrkwks->SetReopenVolumeHandlesTimer();
  1593. }
  1594. //+----------------------------------------------------------------------------
  1595. //
  1596. // CVolume::FileActionIdNotTunnelled
  1597. //
  1598. // This method is called as an event notification, indicating that NTFS has
  1599. // notified us that a file could not be tunnelled. We do the tunnelling manually
  1600. // here.
  1601. //
  1602. //+----------------------------------------------------------------------------
  1603. #define ON_NOT_TUNNELLED_DELAY 500 // .5 seconds
  1604. void
  1605. CVolume::FileActionIdNotTunnelled( FILE_OBJECTID_INFORMATION * poi )
  1606. {
  1607. ULONG ulMillisecondsSleptSoFar = 0;
  1608. HANDLE hFile = NULL;
  1609. // We don't take the volume lock here. So don't attempt to
  1610. // do anything other than simple I/O. We don't take the lock because
  1611. // we want to ensure that tunnelling is resolved quickly without
  1612. // getting blocked.
  1613. //
  1614. // Open the file being "tunnelled from" by OBJECTID
  1615. // Delete the object id
  1616. // Close
  1617. // Open the file being "tunnelled to" by FileReference
  1618. // Set the object id and extra data
  1619. // Close
  1620. // Test hook
  1621. IFDBG( _pTunnelTest->ReleaseAndWait() );
  1622. __try
  1623. {
  1624. if (_hVolume == NULL)
  1625. {
  1626. // Couldn't reopen the volume
  1627. __leave;
  1628. }
  1629. NTSTATUS Status;
  1630. OBJECT_ATTRIBUTES oa;
  1631. UNICODE_STRING uId;
  1632. IO_STATUS_BLOCK ios;
  1633. CObjId objid( FOI_OBJECTID, *poi );
  1634. int i;
  1635. // Ignore if this isn't a link tracking (e.g. it's an NTFRS) object ID.
  1636. if( CObjId() == CObjId(FOI_BIRTHID, *poi) )
  1637. {
  1638. TrkLog(( TRKDBG_VOLUME, TEXT("Ignoring not-tunneled notification for %s"),
  1639. (const TCHAR*)CDebugString( objid ) ));
  1640. __leave;
  1641. }
  1642. uId.Length = sizeof(poi->ObjectId);
  1643. uId.MaximumLength = sizeof(poi->ObjectId);
  1644. uId.Buffer = (PWSTR) poi->ObjectId;
  1645. InitializeObjectAttributes( &oa, &uId, OBJ_CASE_INSENSITIVE, _hVolume, NULL );
  1646. // -----------------
  1647. // Open the old file
  1648. // -----------------
  1649. // Some kind of write access, along with restore privelege, is required
  1650. // for set/delete OID calls.
  1651. EnableRestorePrivilege();
  1652. Status = NtCreateFile(
  1653. &hFile,
  1654. SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
  1655. &oa,
  1656. &ios,
  1657. NULL,
  1658. FILE_ATTRIBUTE_NORMAL,
  1659. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  1660. FILE_OPEN,
  1661. FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL
  1662. | FILE_SYNCHRONOUS_IO_NONALERT,
  1663. NULL,
  1664. 0);
  1665. // ------------------------------
  1666. // Delete the OID on the old file
  1667. // ------------------------------
  1668. if (NT_SUCCESS(Status))
  1669. {
  1670. Status = NtFsControlFile(
  1671. hFile,
  1672. NULL,
  1673. NULL,
  1674. NULL,
  1675. &ios,
  1676. FSCTL_DELETE_OBJECT_ID,
  1677. NULL, // in buffer
  1678. 0, // in buffer size
  1679. NULL, // Out buffer
  1680. 0); // Out buffer size
  1681. NtClose( hFile );
  1682. hFile = NULL;
  1683. if (NT_SUCCESS(Status))
  1684. {
  1685. TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %s - deleted from old file"),
  1686. (const TCHAR*)CDebugString(objid) ));
  1687. }
  1688. else
  1689. {
  1690. TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't FSCTL_DELETE_OBJECT_ID ntstatus=%08x"),
  1691. VolChar(_iVol),
  1692. (const TCHAR*)CDebugString(objid),
  1693. Status ));
  1694. }
  1695. } // if (NT_SUCCESS(Status))
  1696. else
  1697. {
  1698. // We couldn't open the old file, so we'll ignore it and try to set the
  1699. // object ID.
  1700. TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't open old file %08x"),
  1701. VolChar(_iVol),
  1702. (const TCHAR*)CDebugString(objid),
  1703. Status));
  1704. }
  1705. if( Status == STATUS_INVALID_DEVICE_REQUEST ||
  1706. IsErrorDueToLockedVolume( Status ) )
  1707. {
  1708. // If we get STATUS_INVALID_DEVICE_REQUEST, then _hVolume is
  1709. // broken.
  1710. CloseVolumeHandles();
  1711. g_ptrkwks->SetReopenVolumeHandlesTimer();
  1712. __leave;
  1713. }
  1714. // -----------------
  1715. // Open the new file
  1716. // -----------------
  1717. uId.Length = sizeof(poi->FileReference);
  1718. uId.MaximumLength = sizeof(poi->FileReference);
  1719. uId.Buffer = (PWSTR) &poi->FileReference;
  1720. Status = NtCreateFile(
  1721. &hFile,
  1722. SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
  1723. &oa,
  1724. &ios,
  1725. NULL,
  1726. FILE_ATTRIBUTE_NORMAL,
  1727. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  1728. FILE_OPEN,
  1729. FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL
  1730. | FILE_SYNCHRONOUS_IO_NONALERT,
  1731. NULL,
  1732. 0 );
  1733. // ---------------------------
  1734. // Set the OID on the new file
  1735. // ---------------------------
  1736. if (NT_SUCCESS(Status))
  1737. {
  1738. Status = NtFsControlFile(
  1739. hFile,
  1740. NULL,
  1741. NULL,
  1742. NULL,
  1743. &ios,
  1744. FSCTL_SET_OBJECT_ID,
  1745. poi->ObjectId,
  1746. sizeof(FILE_OBJECTID_BUFFER),
  1747. NULL, // Out buffer
  1748. 0); // Out buffer size
  1749. NtClose(hFile);
  1750. hFile = NULL;
  1751. TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %s: FSCTL_SET_OBJECT_ID %s %08x"),
  1752. (const TCHAR*)CDebugString(objid),
  1753. NT_SUCCESS(Status) ? TEXT("succeeded") : TEXT("failed"),
  1754. Status ));
  1755. }
  1756. else
  1757. {
  1758. TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't OpenByFileReference ntstatus=%08x"),
  1759. VolChar(_iVol),
  1760. (const TCHAR*)CDebugString(objid),
  1761. Status ));
  1762. }
  1763. if(Status == STATUS_INVALID_DEVICE_REQUEST ||
  1764. IsErrorDueToLockedVolume( Status ) )
  1765. {
  1766. // If we get STATUS_INVALID_DEVICE_REQUEST, then _hVolume is
  1767. // broken.
  1768. CloseVolumeHandles();
  1769. g_ptrkwks->SetReopenVolumeHandlesTimer();
  1770. __leave;
  1771. }
  1772. }
  1773. __except( BreakOnDebuggableException() )
  1774. {
  1775. TrkLog(( TRKDBG_ERROR, TEXT("Exception %08x in CVolume::FileActionIdNotTunnelled"),
  1776. GetExceptionCode() ));
  1777. }
  1778. if( NULL != hFile )
  1779. NtClose( hFile );
  1780. return;
  1781. }
  1782. //+----------------------------------------------------------------------------
  1783. //
  1784. // CVolume::NotifyAddOrDelete
  1785. //
  1786. // This method is called as an event notification, indicating that NTFS has
  1787. // notified us that the volume ID has been modified. We use this to ensure
  1788. // the volume ID doesn't get incorrectly modified.
  1789. //
  1790. // If you watch the object ID notification queue while someone sets the
  1791. // volume ID directly in NTFS, you'll see:
  1792. // * a remove of the old ID (setting a new ID shows up as a remove/add),
  1793. // * an add of the bogus ID,
  1794. // * a remove of the bogus ID (part of the SetVolid that we do in this routine),
  1795. // * an add of the correct ID.
  1796. //
  1797. //+----------------------------------------------------------------------------
  1798. void
  1799. CVolume::NotifyAddOrDelete( ULONG Action, const CObjId & objid )
  1800. {
  1801. CVolumeId volidCorrect;
  1802. // We don't take the volume lock here. So don't attempt to
  1803. // do anything other than simple I/O. We don't take the lock because
  1804. // we want to ensure the notifications from NTFS don't get backed up
  1805. // (so we don't miss any tunnel notifications).
  1806. // We only hook removes
  1807. if( FILE_ACTION_REMOVED != Action )
  1808. return;
  1809. volidCorrect = GetVolumeId();
  1810. if( volidCorrect == objid && !_fInSetVolIdOnVolume )
  1811. {
  1812. NTSTATUS status = 0;
  1813. status = SetVolIdOnVolume( volidCorrect );
  1814. TrkLog(( TRKDBG_WARNING|TRKDBG_VOLUME,
  1815. TEXT("Undoing delete of volume ID:\n => %s (%08x)"),
  1816. (const TCHAR*)CDebugString( volidCorrect ),
  1817. status ));
  1818. }
  1819. return;
  1820. }
  1821. //+----------------------------------------------------------------------------
  1822. //
  1823. // CVolume::LoadVolInfo
  1824. //
  1825. // Load the _volinfo member from the log.
  1826. //
  1827. //+----------------------------------------------------------------------------
  1828. void
  1829. CVolume::LoadVolInfo()
  1830. {
  1831. AssertLocked();
  1832. TrkAssert( CVOLUME_HEADER_LENGTH == sizeof(_volinfo) );
  1833. // Read _volinfo from the extended header portion of the log.
  1834. __try
  1835. {
  1836. _cLogFile.ReadExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) );
  1837. }
  1838. __except( IsRecoverableDiskError( GetExceptionCode() )
  1839. ? EXCEPTION_EXECUTE_HANDLER
  1840. : EXCEPTION_CONTINUE_SEARCH )
  1841. {
  1842. // Attempt to recover from this error and retry the read.
  1843. if( IsErrorDueToLockedVolume( GetExceptionCode() ))
  1844. {
  1845. CloseAndReopenVolumeHandles();
  1846. _cLogFile.ReadExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) );
  1847. }
  1848. else
  1849. {
  1850. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1851. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1852. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1853. NULL );
  1854. DeleteLogAndReInitializeVolume();
  1855. }
  1856. }
  1857. }
  1858. //+----------------------------------------------------------------------------
  1859. //
  1860. // CVolume::SaveVolInfo
  1861. //
  1862. // Write the _volinfo structure to the extended header portion of the log.
  1863. // Clear _fDirty if successful.
  1864. //
  1865. //+----------------------------------------------------------------------------
  1866. void
  1867. CVolume::SaveVolInfo( )
  1868. {
  1869. AssertLocked();
  1870. __try
  1871. {
  1872. _cLogFile.WriteExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) );
  1873. _fDirty = FALSE;
  1874. }
  1875. __except( IsRecoverableDiskError( GetExceptionCode() )
  1876. ? EXCEPTION_EXECUTE_HANDLER
  1877. : EXCEPTION_CONTINUE_SEARCH )
  1878. {
  1879. // Attempt to recover from this error and retry the write.
  1880. if( IsErrorDueToLockedVolume( GetExceptionCode() ) )
  1881. {
  1882. CloseAndReopenVolumeHandles();
  1883. _cLogFile.WriteExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) );
  1884. _fDirty = FALSE;
  1885. }
  1886. else
  1887. {
  1888. TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() );
  1889. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  1890. static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )),
  1891. NULL );
  1892. DeleteLogAndReInitializeVolume();
  1893. }
  1894. }
  1895. }
  1896. //+----------------------------------------------------------------------------
  1897. //
  1898. // CVolume::CloseVolumeHandles
  1899. //
  1900. // This method close all handle that this object maintains on the volume.
  1901. // This will allow e.g. format or chkdsk /f to run successfully.
  1902. // We don't take the volume critsec, so we're guaranteed to run quickly
  1903. // and not block.
  1904. //
  1905. //+----------------------------------------------------------------------------
  1906. void
  1907. CVolume::CloseVolumeHandles( HDEVNOTIFY hdnVolume, EHandleChangeReason eHandleChangeReason )
  1908. {
  1909. HANDLE hVolToClose = NULL;
  1910. // This routine never raises
  1911. // Is this notification intended for everyone, or specifically
  1912. // for us?
  1913. if( hdnVolume != NULL && hdnVolume != _hdnVolumeLock )
  1914. // No, it's just for another volume.
  1915. return;
  1916. // Are we already in a CloseVolumeHandles somewhere?
  1917. // If so, there's no need to continue, and worse yet if we were
  1918. // to continue we could deadlock (scenario: one thread is in
  1919. // _cLogFile.Close below unregistering the oplock wait, which is
  1920. // blocking, and another thread is executing an oplock break).
  1921. if( !BeginSingleInstanceTask( &_cCloseVolumeHandlesInProgress ) )
  1922. {
  1923. TrkLog(( TRKDBG_VOLUME, TEXT("Skipping CloseVolumeHandles, another instance already in progress") ));
  1924. return;
  1925. }
  1926. // We don't want this method to take the normal _csVolume lock, because
  1927. // we're called from threads that must not block (such as the service control handler
  1928. // thread). So we just take the limited lock that's used in this method
  1929. // and in ReopenVolumeHandles.
  1930. LockHandles();
  1931. __try
  1932. {
  1933. BOOL fAlreadyLockedOrDismounted = _fVolumeLocked || _fVolumeDismounted;
  1934. TrkLog(( TRKDBG_WARNING, TEXT("Closing volume handles on %c:"), VolChar(_iVol) ));
  1935. // If this notification is specifically for us, then remember
  1936. // if we're locked/dismounted.
  1937. if( hdnVolume != NULL )
  1938. {
  1939. if( VOLUME_LOCK_CHANGE == eHandleChangeReason )
  1940. _fVolumeLocked = TRUE;
  1941. else if( VOLUME_MOUNT_CHANGE == eHandleChangeReason )
  1942. _fVolumeDismounted = TRUE;
  1943. }
  1944. // Is this volume already locked or dismounted?
  1945. if( fAlreadyLockedOrDismounted )
  1946. {
  1947. TrkAssert( NULL == _hVolume );
  1948. __leave;
  1949. }
  1950. // Close the object ID index directory handle.
  1951. _ObjIdIndexChangeNotifier.StopListeningAndClose();
  1952. // Close the log.
  1953. _cLogFile.Close(); // Doesn't raise
  1954. // Prepare to close the volume handle.
  1955. if (_hVolume != NULL)
  1956. {
  1957. hVolToClose = _hVolume;
  1958. _hVolume = NULL;
  1959. }
  1960. TrkLog((TRKDBG_VOLUME, TEXT("Volume %c: closed"), VolChar(_iVol)));
  1961. }
  1962. __except( EXCEPTION_EXECUTE_HANDLER ) // BreakThenReturn( EXCEPTION_EXECUTE_HANDLER ))
  1963. {
  1964. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring unexpected exception in CVolume::CloseVolumeHandles (%08x)"),
  1965. GetExceptionCode() ));
  1966. }
  1967. UnlockHandles();
  1968. EndSingleInstanceTask( &_cCloseVolumeHandlesInProgress );
  1969. if( NULL != hVolToClose )
  1970. {
  1971. NtClose( hVolToClose );
  1972. TrkLog((TRKDBG_VOLUME, TEXT("(Volume %c: fully closed)"), VolChar(_iVol)));
  1973. }
  1974. }
  1975. //+----------------------------------------------------------------------------
  1976. //
  1977. // CVolume::ReopenVolumeHandles
  1978. //
  1979. // Reopen the handles that we maintain on the volume. This is synchronized
  1980. // with CloseVolumeHandles using the handle critsec. This method does
  1981. // nothing if the volume is locked (as indicated by _fVolumeLocked),
  1982. // or if the volume handles are already opened.
  1983. //
  1984. //+----------------------------------------------------------------------------
  1985. BOOL
  1986. CVolume::ReopenVolumeHandles()
  1987. {
  1988. NTSTATUS status;
  1989. BOOL fHandlesLocked = FALSE;
  1990. BOOL fHandlesOpen = FALSE;
  1991. BOOL fReopenedLog = FALSE;
  1992. BOOL fStartedListening = FALSE;
  1993. // Don't open if the service is stopping.
  1994. g_ptrkwks->RaiseIfStopped();
  1995. TrkLog(( TRKDBG_WARNING, TEXT("\nReopenVolumeHandles called on %c:"), VolChar(_iVol) ));
  1996. // This method must acquire the _csVolumes critical section like every other
  1997. // public method (via the Lock call). It must also acquire the _csHandles
  1998. // lock, in order to coordinate with the CloseVolumeHandles and SetUnlockVolume
  1999. // methods, which have special needs.
  2000. Lock();
  2001. LockHandles(); fHandlesLocked = TRUE;
  2002. __try
  2003. {
  2004. // Are we supposed to reopen?
  2005. if( IsHandsOffVolumeMode() )
  2006. {
  2007. // Don't open yet, wait until we get an UnLock notification.
  2008. TrkLog(( TRKDBG_VOLUME, TEXT("Didn't open handles on %c:, it's %s"),
  2009. VolChar(_iVol),
  2010. _fVolumeLocked
  2011. ? ( _fVolumeDismounted ? TEXT("locked & dismounted") : TEXT("locked") )
  2012. : TEXT("dismounted") ));
  2013. __leave;
  2014. }
  2015. // Open the main volume handle.
  2016. if( NULL == _hVolume )
  2017. {
  2018. status = OpenVolume(_tszVolumeDeviceName, &_hVolume);
  2019. if(!NT_SUCCESS(status))
  2020. {
  2021. if( STATUS_OBJECT_NAME_NOT_FOUND == status )
  2022. {
  2023. TrkLog(( TRKDBG_VOLUME, TEXT("Volume not found in ReopenVolumeHandles, deleting CVolume") ));
  2024. MarkSelfForDelete();
  2025. }
  2026. TrkRaiseNtStatus(status);
  2027. }
  2028. }
  2029. // Start listening for objid index change notifications
  2030. __try
  2031. {
  2032. fStartedListening = _ObjIdIndexChangeNotifier.AsyncListen( );
  2033. }
  2034. __except( BreakOnDebuggableException() )
  2035. {
  2036. // We should never get a path-not-found error, because meta-files always exist
  2037. // on a good NTFS5 volume. If we get one, it's probably because an NTFS5
  2038. // volume has been reformatted as a FAT volume.
  2039. HRESULT hr = GetExceptionCode();
  2040. if( HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr )
  2041. {
  2042. TrkLog(( TRKDBG_VOLUME, TEXT("ObjId Index not found in ReopenVolumeHandles, deleting CVolume") ));
  2043. MarkSelfForDelete();
  2044. }
  2045. TrkRaiseException( hr );
  2046. }
  2047. // Register for PNP notifications. We don't hold the handle lock because this
  2048. // call can take a while, and we don't want to block the service control
  2049. // handler thread from doing a CloseVolumeHandles.
  2050. if( fStartedListening )
  2051. {
  2052. #if DBG
  2053. TrkVerify( 0 == UnlockHandles() );
  2054. #else
  2055. UnlockHandles();
  2056. #endif
  2057. fHandlesLocked = FALSE;
  2058. RegisterPnPVolumeNotification();
  2059. LockHandles(); fHandlesLocked = TRUE;
  2060. }
  2061. // If CloseVolumeHandles came in after we released the handle lock just now,
  2062. // then abort.
  2063. if( NULL == _hVolume )
  2064. {
  2065. TrkLog(( TRKDBG_VOLUME, TEXT("Aborting ReopenVolumeHandles") ));
  2066. TrkRaiseException( E_FAIL );
  2067. }
  2068. // Open the log
  2069. if( !_cLogFile.IsOpen() )
  2070. {
  2071. for( int i = 0; i < 2; i++ )
  2072. {
  2073. __try
  2074. {
  2075. _cLogFile.Initialize( _tszVolumeDeviceName, _pTrkWksConfiguration, this, VolChar(_iVol) );
  2076. _cLog.Initialize( _pLogCallback, _pTrkWksConfiguration, &_cLogFile );
  2077. }
  2078. __except( (0 == i && TRK_E_CORRUPT_LOG == GetExceptionCode())
  2079. ? EXCEPTION_EXECUTE_HANDLER
  2080. : EXCEPTION_CONTINUE_SEARCH )
  2081. {
  2082. BreakIfRequested();
  2083. // Get rid of the corrupt file
  2084. _cLogFile.Delete();
  2085. // Loop back and try again.
  2086. continue;
  2087. }
  2088. break;
  2089. }
  2090. fReopenedLog = TRUE;
  2091. }
  2092. // If we've never read in the volinfo, do so now.
  2093. if( !_fVolInfoInitialized )
  2094. {
  2095. LoadVolInfo();
  2096. _fVolInfoInitialized = TRUE;
  2097. TrkLog(( TRKDBG_VOLUME, TEXT("VolId (from log file) for %c: is %s"),
  2098. VolChar(_iVol), (const TCHAR*)CDebugString(_volinfo.volid) ));
  2099. }
  2100. if( fReopenedLog )
  2101. {
  2102. // Reconcile our volinfo with the log.
  2103. // BUGBUG (removable media): We haven't re-read the volinfo out of the log, we're still
  2104. // using what we already had in memory. This won't work for removeable
  2105. // media, so we need to add some extra checking here.
  2106. VolumeSanityCheck();
  2107. // Start the move notify timer; we may have been trying to send
  2108. // notifies when we discovered that the volume handles were bad.
  2109. g_ptrkwks->OnEntriesAvailable();
  2110. }
  2111. TrkLog((TRKDBG_VOLUME, TEXT("Volume %c open"), VolChar(_iVol)));
  2112. fHandlesOpen = TRUE;
  2113. }
  2114. __finally
  2115. {
  2116. if( fHandlesLocked )
  2117. UnlockHandles();
  2118. // We either open everything, or open nothing
  2119. if( AbnormalTermination() )
  2120. {
  2121. CloseVolumeHandles(); // Doesn't raise
  2122. g_ptrkwks->SetReopenVolumeHandlesTimer(); // Try again later
  2123. }
  2124. Unlock();
  2125. }
  2126. return( fHandlesOpen );
  2127. }
  2128. //+----------------------------------------------------------------------------
  2129. //
  2130. // CVolume::CloseAndReopenVolumeHandles
  2131. //
  2132. // One or more of the handles maintained on the volume are bad (for example,
  2133. // the handle may have been broken by a dismount). Close all of them, and
  2134. // attempt to reopen new ones.
  2135. //
  2136. //+----------------------------------------------------------------------------
  2137. void
  2138. CVolume::CloseAndReopenVolumeHandles()
  2139. {
  2140. Lock();
  2141. __try
  2142. {
  2143. // There's the remote possibility that the ReopenVolumeHandles
  2144. // call below will call this routine. Just to be paranoid, we
  2145. // add protection against an infinite recursion.
  2146. if( _fCloseAndReopenVolumeHandles )
  2147. TrkRaiseWin32Error( ERROR_OPEN_FAILED );
  2148. _fCloseAndReopenVolumeHandles = TRUE;
  2149. // Close then reopen the handles
  2150. CloseVolumeHandles();
  2151. ReopenVolumeHandles();
  2152. }
  2153. __except( BreakOnDebuggableException() )
  2154. {
  2155. TrkLog((TRKDBG_VOLUME, TEXT("Immediate reopen of volume handle failed, set the reopen timer")));
  2156. g_ptrkwks->SetReopenVolumeHandlesTimer();
  2157. }
  2158. _fCloseAndReopenVolumeHandles = FALSE;
  2159. Unlock();
  2160. }
  2161. //+----------------------------------------------------------------------------
  2162. //
  2163. // CVolume::PrepareToReopenVolumeHandles
  2164. //
  2165. // The handles to the volume were closed at some point, but they may now
  2166. // be reopened. Call CVolumeManager::OnVolumeToBeReopened, so that it can
  2167. // call us in ReopenVolumeHandles on a worker thread (right now we're on
  2168. // the services handler thread, which is shared by all of services.exe).
  2169. //
  2170. //+----------------------------------------------------------------------------
  2171. void
  2172. CVolume::PrepareToReopenVolumeHandles( HDEVNOTIFY hdnVolume, EHandleChangeReason eHandleChangeReason )
  2173. {
  2174. // It is important that this method does not take the volume lock.
  2175. // This method is called during the volume unlock notification
  2176. // that we receive in CTrkWksSvc::ServiceHandler, and we can
  2177. // never allow that thread to hang.
  2178. LockHandles();
  2179. if( _hdnVolumeLock == hdnVolume )
  2180. {
  2181. if( VOLUME_LOCK_CHANGE == eHandleChangeReason )
  2182. _fVolumeLocked = FALSE;
  2183. else if( VOLUME_MOUNT_CHANGE == eHandleChangeReason )
  2184. _fVolumeDismounted = FALSE;
  2185. if( !_fVolumeLocked && !_fVolumeDismounted )
  2186. {
  2187. TrkLog(( TRKDBG_VOLUME, TEXT("Volume %c: is to be reopened"), VolChar(_iVol) ));
  2188. _pVolMgr->OnVolumeToBeReopened();
  2189. }
  2190. }
  2191. UnlockHandles();
  2192. return;
  2193. }