Leaked source code of windows server 2003
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.

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