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.

2648 lines
88 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997-1999.
  5. //
  6. // File: usnmgr.cxx
  7. //
  8. // Contents: Usn manager
  9. //
  10. // History: 07-May-97 SitaramR Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <pch.cxx>
  14. #pragma hdrstop
  15. #include <eventlog.hxx>
  16. #include <cievtmsg.h>
  17. #include <smatch.hxx>
  18. #include <cifrmcom.hxx>
  19. #include "cicat.hxx"
  20. #include "notifmgr.hxx"
  21. #include "usnmgr.hxx"
  22. #include "usntree.hxx"
  23. //
  24. // Local constants
  25. //
  26. unsigned const USN_LOG_DANGER_THRESHOLD = 50;
  27. //+---------------------------------------------------------------------------
  28. //
  29. // Member: CUsnMgr::CUsnMgr
  30. //
  31. // Synopsis: Constructor
  32. //
  33. // Arguments: [cicat] -- Catalog
  34. //
  35. // History: 07-May-97 SitaramR Created
  36. //
  37. //----------------------------------------------------------------------------
  38. CUsnMgr::CUsnMgr( CiCat & cicat )
  39. : _cicat(cicat),
  40. _fAbort(FALSE),
  41. _fBatch(FALSE),
  42. #pragma warning( disable : 4355 ) // this used in base initialization
  43. _thrUsn(UsnThread, this, TRUE),
  44. #pragma warning( default : 4355 )
  45. _fUpdatesDisabled(FALSE),
  46. _fDoingRenameTraverse( FALSE ),
  47. _waitForMultObj(1+RTL_MAX_DRIVE_LETTERS), // 1 for _evtUsn + RTL_MAX_DRIVE_LETTERS drives (a: to z: + few special chars)
  48. _fWaitingForUpdates( FALSE )
  49. {
  50. _evtUsn.Reset();
  51. _thrUsn.SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
  52. }
  53. //+---------------------------------------------------------------------------
  54. //
  55. // Member: CUsnMgr::~CUsnMgr
  56. //
  57. // Synopsis: Destructor
  58. //
  59. // History: 07-May-97 SitaramR Created
  60. //
  61. //----------------------------------------------------------------------------
  62. CUsnMgr::~CUsnMgr()
  63. {
  64. InitiateShutdown();
  65. WaitForShutdown();
  66. }
  67. //+---------------------------------------------------------------------------
  68. //
  69. // Member: CUsnMgr::GetMaxUSNs, public
  70. //
  71. // Synopsis: Updates or adds entries to flushInfoList to reflect the max
  72. // usn processed for the volumes.
  73. //
  74. // History: 07-May-97 SitaramR Created
  75. //
  76. //----------------------------------------------------------------------------
  77. void CUsnMgr::GetMaxUSNs( CUsnFlushInfoList & flushInfoList )
  78. {
  79. for ( CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
  80. !_usnVolumesToMonitor.AtEnd( usnVolIter );
  81. _usnVolumesToMonitor.Advance( usnVolIter ) )
  82. {
  83. //
  84. // Look for the volume and store the highest usn if found.
  85. //
  86. BOOL fFound = FALSE;
  87. for ( unsigned i = 0; i < flushInfoList.Count(); i++ )
  88. {
  89. CUsnFlushInfo & info = * ( flushInfoList.Get( i ) );
  90. if ( usnVolIter->VolumeId() == info.volumeId )
  91. {
  92. ciDebugOut(( DEB_ITRACE,
  93. "GetMaxUSNs updating vol %wc from %#I64x to %#I64x\n",
  94. usnVolIter->DriveLetter(),
  95. info.usnHighest,
  96. usnVolIter->MaxUsnRead() ));
  97. info.usnHighest = usnVolIter->MaxUsnRead();
  98. fFound = TRUE;
  99. break;
  100. }
  101. }
  102. //
  103. // If the volume isn't in the list yet, add it.
  104. //
  105. if ( !fFound )
  106. {
  107. ciDebugOut(( DEB_ITRACE,
  108. "GetMaxUSNs adding vol %wc %#I64x\n",
  109. usnVolIter->DriveLetter(),
  110. usnVolIter->MaxUsnRead() ));
  111. XPtr<CUsnFlushInfo> xInfo( new CUsnFlushInfo( usnVolIter->VolumeId(),
  112. usnVolIter->MaxUsnRead() ) );
  113. flushInfoList.Add( xInfo.GetPointer(), i );
  114. xInfo.Acquire();
  115. }
  116. }
  117. } //GetMaxUSNs
  118. //+---------------------------------------------------------------------------
  119. //
  120. // Member: CUsnMgr::AddScope
  121. //
  122. // Synopsis: Scans scope and then monitors scope for usn notifications
  123. //
  124. // Arguments: [pwcsScope] -- Scope
  125. // [volumeId] -- Volume id
  126. // [fDoDeletions] -- Should delete processing be done ?
  127. // [usnStart] -- "High water mark" of previous activity
  128. // [fFullScan] -- TRUE if we should start over from scratch
  129. // [fUserInitiatedScan] -- TRUE if the user asked for the scan
  130. // [fNewScope] -- TRUE if scope was just added to the catalog
  131. //
  132. // History: 07-May-97 SitaramR Created
  133. //
  134. //----------------------------------------------------------------------------
  135. void CUsnMgr::AddScope( WCHAR const *pwcsScope,
  136. VOLUMEID volumeId,
  137. BOOL fDoDeletions,
  138. USN const & usnStart,
  139. BOOL fFullScan,
  140. BOOL fUserInitiatedScan,
  141. BOOL fNewScope )
  142. {
  143. Win4Assert( wcslen(pwcsScope) < MAX_PATH );
  144. XPtr<CCiScanInfo> xScanInfo( QueryScanInfo( pwcsScope,
  145. volumeId,
  146. usnStart,
  147. fDoDeletions,
  148. fUserInitiatedScan,
  149. fNewScope ) );
  150. xScanInfo->SetScan();
  151. if ( fFullScan )
  152. xScanInfo->SetFullUsnScan();
  153. ScanScope( xScanInfo, FALSE );
  154. } //AddScope
  155. //+---------------------------------------------------------------------------
  156. //
  157. // Member: CUsnMgr::MonitorScope
  158. //
  159. // Synopsis: Monitors scope for usn notifications, i.e. no scan is done
  160. //
  161. // Arguments: [pwcsScope] - Scope
  162. // [volumeId] - Volume id
  163. // [usnStart] - Usn to start monitoring from
  164. //
  165. // History: 07-May-97 SitaramR Created
  166. //
  167. //----------------------------------------------------------------------------
  168. void CUsnMgr::MonitorScope( WCHAR const *pwcsScope,
  169. VOLUMEID volumeId,
  170. USN usnStart )
  171. {
  172. Win4Assert( wcslen(pwcsScope) < MAX_PATH );
  173. XPtr<CCiScanInfo> xScanInfo( QueryScanInfo( pwcsScope,
  174. volumeId,
  175. usnStart,
  176. FALSE ) );
  177. xScanInfo->SetMonitorOnly();
  178. ScanScope( xScanInfo, FALSE );
  179. } //MonitorScope
  180. //+---------------------------------------------------------------------------
  181. //
  182. // Member: CUsnMgr::RemoveScope
  183. //
  184. // Synopsis: Removes usn path from catalog
  185. //
  186. // Arguments: [pwscScope] - Scope to be removed
  187. // [volumeId] - Volume id
  188. //
  189. // History: 07-May-97 SitaramR Created
  190. //
  191. //----------------------------------------------------------------------------
  192. void CUsnMgr::RemoveScope( WCHAR const *pwcsScope, VOLUMEID volumeId )
  193. {
  194. //
  195. // If the given scope is in the list of paths being currently
  196. // scanned, we must mark it deleted.
  197. //
  198. BOOL fRemoved = FALSE;
  199. {
  200. CLock lock(_mutex);
  201. if ( _fAbort || _fUpdatesDisabled )
  202. return;
  203. for ( CFwdScanInfoIter scanInfoIter1( _usnScansToDo );
  204. !_usnScansToDo.AtEnd( scanInfoIter1 );
  205. _usnScansToDo.Advance( scanInfoIter1 ) )
  206. {
  207. WCHAR const * pwcsPath = scanInfoIter1->GetPath();
  208. if ( AreIdenticalPaths( pwcsScope, pwcsPath ) )
  209. {
  210. fRemoved = TRUE;
  211. if ( !scanInfoIter1->LokIsDelScope() )
  212. scanInfoIter1->LokSetDelScope();
  213. }
  214. }
  215. if ( !fRemoved )
  216. {
  217. LokScheduleRemove( pwcsScope, volumeId );
  218. fRemoved = TRUE;
  219. }
  220. {
  221. CLock lock(_mutex);
  222. if ( !_fBatch )
  223. _evtUsn.Set(); // wake up the usn thread.
  224. }
  225. Win4Assert( fRemoved );
  226. }
  227. } //RemoveScope
  228. //+---------------------------------------------------------------------------
  229. //
  230. // Member: CUsnMgr::ScanScope
  231. //
  232. // Synopsis: Scan the given scopes and then start monitoring for usn
  233. // notifications
  234. //
  235. // Arguments: [xScanInfo] - Scan info
  236. // [fRefiled] - Is this a refiled scan ?
  237. //
  238. // History: 07-May-97 SitaramR Created
  239. //
  240. //----------------------------------------------------------------------------
  241. void CUsnMgr::ScanScope( XPtr<CCiScanInfo> & xScanInfo,
  242. BOOL fRefiled )
  243. {
  244. CLock lock(_mutex);
  245. #if CIDBG==1
  246. //
  247. // Debug code to track down the cause of _fUsnTreeScan assert
  248. //
  249. if ( xScanInfo->LokIsInScan() )
  250. _cicat.CheckUsnTreeScan( xScanInfo->GetPath() );
  251. #endif
  252. ciDebugOut(( DEB_ITRACE, "CUsnMgr::ScanScope, retries %d\n",
  253. xScanInfo->GetRetries() ));
  254. if ( xScanInfo->GetRetries() > CCiScanInfo::MAX_RETRIES )
  255. {
  256. //
  257. // Log event that scan failed, cleanup and then return
  258. //
  259. CEventLog eventLog( NULL, wcsCiEventSource );
  260. CEventItem item( EVENTLOG_ERROR_TYPE,
  261. CI_SERVICE_CATEGORY,
  262. MSG_CI_CONTENTSCAN_FAILED,
  263. 1 );
  264. item.AddArg( xScanInfo->GetPath() );
  265. eventLog.ReportEvent( item );
  266. CCiScanInfo * pScanInfo = xScanInfo.Acquire();
  267. delete pScanInfo;
  268. return;
  269. }
  270. if ( LokIsScanScheduled( xScanInfo ) )
  271. {
  272. ciDebugOut(( DEB_ITRACE, "CUsnMgr::ScanScope scan was already scheduled\n" ));
  273. CCiScanInfo * pScanInfo = xScanInfo.Acquire();
  274. delete pScanInfo;
  275. }
  276. else
  277. {
  278. Win4Assert( !xScanInfo->LokIsInFinalState() );
  279. Win4Assert( xScanInfo->LokIsInScan()
  280. || xScanInfo->LokIsInFullUsnScan()
  281. || xScanInfo->LokIsDelScope()
  282. || xScanInfo->LokIsRenameDir()
  283. || xScanInfo->LokIsMonitorOnly() );
  284. ciDebugOut(( DEB_ITRACE,
  285. "CUsnMgr::ScanScope scan wasn't already scheduled, fRefiled: %d\n",
  286. fRefiled ));
  287. if ( fRefiled )
  288. {
  289. //
  290. // A scan that has been refiled should be done before new scans to
  291. // ensure that all scans are done in FIFO order
  292. //
  293. _usnScansToDo.Push( xScanInfo.GetPointer() );
  294. }
  295. else
  296. _usnScansToDo.Queue( xScanInfo.GetPointer() );
  297. xScanInfo.Acquire();
  298. if ( !_fBatch )
  299. _evtUsn.Set();
  300. }
  301. } //ScanScope
  302. //+---------------------------------------------------------------------------
  303. //
  304. // Member: CUsnMgr::LokIsScanScheduled
  305. //
  306. // Synopsis: Tests if the given scope is already scheduled for a scan.
  307. //
  308. // Arguments: [xScanInfo] - Smart pointer to scaninfo
  309. //
  310. // History: 07-May-97 SitaramR Created
  311. //
  312. //----------------------------------------------------------------------------
  313. BOOL CUsnMgr::LokIsScanScheduled( const XPtr<CCiScanInfo> & xScanInfo )
  314. {
  315. WCHAR const * pwszNewScope = xScanInfo->GetPath();
  316. unsigned lenNewScope = wcslen( pwszNewScope );
  317. for ( CFwdScanInfoIter scanInfoIter(_usnScansToDo);
  318. !_usnScansToDo.AtEnd(scanInfoIter);
  319. _usnScansToDo.Advance(scanInfoIter) )
  320. {
  321. if ( xScanInfo->LokGetWorkType() == scanInfoIter->LokGetWorkType() )
  322. {
  323. WCHAR const * pwszPath = scanInfoIter->GetPath();
  324. CScopeMatch scope( pwszPath, wcslen(pwszPath) );
  325. if (scope.IsInScope( pwszNewScope, lenNewScope ))
  326. {
  327. ciDebugOut(( DEB_WARN,
  328. "Usn scan already scheduled for (%ws)\n",
  329. pwszNewScope ));
  330. return TRUE;
  331. }
  332. }
  333. }
  334. return FALSE;
  335. } //LokIsScanScheduled
  336. //+---------------------------------------------------------------------------
  337. //
  338. // Member: CUsnMgr::QueryScanInfo, private
  339. //
  340. // Synopsis: Returns a new instance of CCiScanInfo
  341. //
  342. // Arguments: [pwcsScope] -- Scope
  343. // [volumeId] -- Volume id
  344. // [usnStart] -- Start usn
  345. // [fDoDeletions] -- Shoud deletions be done ?
  346. // [fUserInitiatedScan] -- TRUE if the user asked for the scan
  347. // [fNewScope] -- TRUE if scope was just added to the catalog
  348. //
  349. // History: 05-May-97 SitaramR Created
  350. //
  351. //----------------------------------------------------------------------------
  352. CCiScanInfo * CUsnMgr::QueryScanInfo( WCHAR const * pwcsScope,
  353. VOLUMEID volumeId,
  354. USN usnStart,
  355. BOOL fDoDeletions,
  356. BOOL fUserInitiatedScan,
  357. BOOL fNewScope )
  358. {
  359. Win4Assert( 0 != pwcsScope );
  360. //
  361. // Check for remote path
  362. //
  363. Win4Assert( pwcsScope[0] != L'\\' );
  364. ULONG len = wcslen( pwcsScope );
  365. Win4Assert( pwcsScope[len-1] == L'\\' );
  366. XArray<WCHAR> xPath( len+1 );
  367. WCHAR * pwcsLocal = xPath.Get();
  368. RtlCopyMemory( pwcsLocal, pwcsScope, (len+1)*sizeof(WCHAR) );
  369. CCiScanInfo * pScanInfo = new CCiScanInfo( xPath,
  370. 1,
  371. UPD_FULL,
  372. fDoDeletions,
  373. volumeId,
  374. usnStart,
  375. fUserInitiatedScan,
  376. fNewScope );
  377. return pScanInfo;
  378. } //QueryScanInfo
  379. //+---------------------------------------------------------------------------
  380. //
  381. // Member: CUsnMgr::DisableUpdates
  382. //
  383. // Synopsis: Disables all usn updates
  384. //
  385. // History: 07-May-97 SitaramR Created
  386. //
  387. //----------------------------------------------------------------------------
  388. void CUsnMgr::DisableUpdates()
  389. {
  390. CLock lock( _mutex );
  391. _fUpdatesDisabled = TRUE;
  392. _evtUsn.Set();
  393. }
  394. //+---------------------------------------------------------------------------
  395. //
  396. // Member: CUsnMgr::EnableUpdates
  397. //
  398. // Synopsis: Enables usn updates
  399. //
  400. // History: 07-May-97 SitaramR Created
  401. //
  402. //----------------------------------------------------------------------------
  403. void CUsnMgr::EnableUpdates()
  404. {
  405. CLock lock( _mutex );
  406. if ( _fUpdatesDisabled )
  407. {
  408. _fUpdatesDisabled = FALSE;
  409. _evtUsn.Set();
  410. }
  411. }
  412. //+---------------------------------------------------------------------------
  413. //
  414. // Member: CUsnMgr::InitiateShutdown
  415. //
  416. // Synopsis: Initiates the shutdown process
  417. //
  418. // History: 05-May-97 SitaramR Created
  419. //
  420. //----------------------------------------------------------------------------
  421. void CUsnMgr::InitiateShutdown()
  422. {
  423. CLock lock(_mutex);
  424. _fAbort = TRUE;
  425. _fUpdatesDisabled = TRUE;
  426. _usnScansToDo.Clear();
  427. _evtUsn.Set();
  428. }
  429. //+---------------------------------------------------------------------------
  430. //
  431. // Member: CUsnMgr::WaitForShutdown
  432. //
  433. // Synopsis: Waits for shutdown to complete
  434. //
  435. // History: 05-May-97 SitaramR Created
  436. //
  437. //----------------------------------------------------------------------------
  438. void CUsnMgr::WaitForShutdown()
  439. {
  440. //
  441. // If we never started running, then just bail out.
  442. //
  443. if ( _thrUsn.IsRunning() )
  444. {
  445. ciDebugOut(( DEB_ITRACE, "Waiting for death of usn thread\n" ));
  446. _thrUsn.WaitForDeath();
  447. }
  448. }
  449. //+---------------------------------------------------------------------------
  450. //
  451. // Member: CUsnMgr::LokEmptyInProgressScans
  452. //
  453. // Synopsis: Removes all the scans from the "in-progress stack" and either
  454. // deletes them or re-schedules them. If the scan is in its
  455. // "terminal state", the scan is deleted. If there is a retry
  456. // it will be re-scheduled.
  457. //
  458. // History: 07-May-97 SitaramR Created
  459. //
  460. //----------------------------------------------------------------------------
  461. void CUsnMgr::LokEmptyInProgressScans()
  462. {
  463. //
  464. // Refile any scopes that still need to be worked on
  465. //
  466. while ( _usnScansInProgress.Count() > 0 )
  467. {
  468. if ( _fAbort )
  469. break;
  470. XPtr<CCiScanInfo> xScanInfo( _usnScansInProgress.RemoveLast() );
  471. if ( _fUpdatesDisabled )
  472. {
  473. CCiScanInfo * pScanInfo = xScanInfo.Acquire();
  474. delete pScanInfo;
  475. }
  476. else if ( xScanInfo->LokIsInFinalState() )
  477. {
  478. //
  479. // Scan of scope completed successfully, so start monitoring
  480. //
  481. LokAddScopeForUsnMonitoring( xScanInfo );
  482. }
  483. else
  484. {
  485. #if CIDBG==1
  486. if ( xScanInfo->LokIsDelScope() )
  487. {
  488. ciDebugOut(( DEB_ITRACE,
  489. "Requeing scope (%ws) for removal\n",
  490. xScanInfo->GetPath() ));
  491. }
  492. else
  493. {
  494. ciDebugOut(( DEB_ITRACE,
  495. "Requeuing scope (%ws) for scan\n",
  496. xScanInfo->GetPath() ));
  497. }
  498. #endif // CIDBG==1
  499. ScanScope( xScanInfo, TRUE ); // It's a refiled scan
  500. }
  501. }
  502. } //LokEmptyInProgressScans
  503. //+---------------------------------------------------------------------------
  504. //
  505. // Member: CUsnMgr::LokScheduleRemove
  506. //
  507. // Synopsis: Schedules a usn path for removal
  508. //
  509. // Arguments: [pwscScope] - Scope to be removed
  510. // [volumeId] - Volume id
  511. //
  512. // History: 07-May-97 SitaramR Created
  513. //
  514. //----------------------------------------------------------------------------
  515. void CUsnMgr::LokScheduleRemove( WCHAR const * pwcsScope,
  516. VOLUMEID volumeId )
  517. {
  518. CCiScanInfo * pScanInfo = QueryScanInfo( pwcsScope,
  519. volumeId,
  520. 0,
  521. TRUE );
  522. XPtr<CCiScanInfo> xScanInfo( pScanInfo );
  523. pScanInfo->LokSetDelScope();
  524. ScanScope( xScanInfo, FALSE );
  525. }
  526. //+---------------------------------------------------------------------------
  527. //
  528. // Member: CUnsMgr::UsnThread
  529. //
  530. // Synopsis: Usn thread start routine
  531. //
  532. // Arguments: [self] - This pointer
  533. //
  534. // History: 07-May-97 SitaramR Created
  535. //
  536. //----------------------------------------------------------------------------
  537. DWORD CUsnMgr::UsnThread( void * self )
  538. {
  539. SCODE sc = CoInitializeEx( 0, COINIT_MULTITHREADED );
  540. ((CUsnMgr *) self)->DoUsnProcessing();
  541. CoUninitialize();
  542. ciDebugOut(( DEB_ITRACE, "Terminating usn thread\n" ));
  543. return 0;
  544. } //UsnThread
  545. //+---------------------------------------------------------------------------
  546. //
  547. // Member: CUsnMgr::DoUsnProcessing
  548. //
  549. // Synopsis: Main loop of usn thread
  550. //
  551. // History: 07-May-97 SitaramR Created
  552. //
  553. //----------------------------------------------------------------------------
  554. void CUsnMgr::DoUsnProcessing()
  555. {
  556. CImpersonateSystem impersonate;
  557. while ( TRUE )
  558. {
  559. //
  560. // Don't do any work until the system has booted
  561. //
  562. while ( GetTickCount() < _cicat.GetRegParams()->GetStartupDelay() )
  563. {
  564. Sleep( 200 );
  565. if ( _fAbort )
  566. break;
  567. }
  568. NTSTATUS status = STATUS_SUCCESS;
  569. TRY
  570. {
  571. XPtr<CCiScanInfo> xScanInfo;
  572. //--------------------------------------------------------------------
  573. {
  574. CLock lock( _mutex );
  575. if ( _fAbort )
  576. break;
  577. if ( !_fBatch )
  578. {
  579. //
  580. // If requests are being batched, then don't process
  581. // requests until the _fBatch flag is cleared.
  582. //
  583. // Refile any incomplete paths that could not be processed due to
  584. // low resources
  585. //
  586. if ( _usnScansInProgress.Count() > 0 )
  587. LokEmptyInProgressScans();
  588. while ( _usnScansToDo.Count() > 0 )
  589. {
  590. xScanInfo.Set( _usnScansToDo.Pop() );
  591. if ( _fUpdatesDisabled )
  592. {
  593. delete xScanInfo.Acquire();
  594. break;
  595. }
  596. else
  597. {
  598. Win4Assert( !xScanInfo->LokIsInFinalState() );
  599. _usnScansInProgress.Queue( xScanInfo.GetPointer() );
  600. xScanInfo.Acquire();
  601. }
  602. }
  603. if ( _fUpdatesDisabled )
  604. {
  605. //
  606. // Empty usn lists, because usns notifications
  607. // are not needed until updates are enabled.
  608. //
  609. _usnScansToDo.Clear();
  610. _usnScansInProgress.Clear();
  611. _usnVolumesToMonitor.Clear();
  612. }
  613. _evtUsn.Reset();
  614. }
  615. }
  616. //--------------------------------------------------------------------
  617. if ( _usnScansInProgress.Count() > 0 )
  618. {
  619. DoUsnScans();
  620. Win4Assert( _usnScansInProgress.Count() == 0
  621. || _fAbort
  622. || _fUpdatesDisabled );
  623. }
  624. //
  625. // ProcessUsnNotifications waits for usn notifications and processes
  626. // them, or it waits until evtUsn is set. Only do this if there
  627. // are no more scans to do.
  628. //
  629. if ( 0 == _usnScansToDo.Count() )
  630. ProcessUsnNotifications();
  631. }
  632. CATCH (CException, e)
  633. {
  634. ciDebugOut(( DEB_ERROR,
  635. "CUsnMgr::DoUsnProcessing, caught exception 0x%X\n",
  636. e.GetErrorCode() ));
  637. status = e.GetErrorCode();
  638. }
  639. END_CATCH
  640. if ( status == CI_CORRUPT_CATALOG || status == CI_CORRUPT_DATABASE )
  641. {
  642. //
  643. // Disable further updates until corruption is cleared
  644. //
  645. CLock lock( _mutex );
  646. _fUpdatesDisabled = TRUE;
  647. }
  648. }
  649. } //DoUsnProcessing
  650. //+---------------------------------------------------------------------------
  651. //
  652. // Member: CUsnMgr::DoUsnScans
  653. //
  654. // Synopsis: Does tree walk for any usn scopes that were added
  655. //
  656. // History: 07-May-97 SitaramR Created
  657. //
  658. //----------------------------------------------------------------------------
  659. void CUsnMgr::DoUsnScans()
  660. {
  661. //
  662. // No need for a lock because only this thread modifies _usnScansInProgress
  663. //
  664. for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
  665. !_usnScansInProgress.AtEnd(scanInfoIter);
  666. _usnScansInProgress.Advance(scanInfoIter) )
  667. {
  668. if ( _fAbort || _fUpdatesDisabled )
  669. break;
  670. CCiScanInfo *pScanInfo = scanInfoIter.GetEntry();
  671. Win4Assert( pScanInfo->GetRetries() <= CCiScanInfo::MAX_RETRIES );
  672. NTSTATUS status = STATUS_SUCCESS;
  673. TRY
  674. {
  675. ICiManager *pCiManager = _cicat.CiManager();
  676. if ( pScanInfo->LokIsDelScope() )
  677. {
  678. _cicat.RemovePathsFromCiCat( pScanInfo->GetPath(), eUsnsArray );
  679. if ( !_fAbort && !_fUpdatesDisabled )
  680. {
  681. SCODE sc = pCiManager->FlushUpdates();
  682. if ( FAILED(sc) )
  683. THROW( CException( sc ) );
  684. pScanInfo->LokSetDone();
  685. }
  686. }
  687. else if ( pScanInfo->LokIsMonitorOnly() )
  688. {
  689. //
  690. // The scope needs to be monitored only and so set it to done
  691. //
  692. pScanInfo->LokSetDone();
  693. }
  694. else
  695. {
  696. //
  697. // A starting USN from 0 means this is a first scan, or a
  698. // complete recovery. A starting USN > 0 means we are
  699. // looking for items changed by NT4 and not recorded in
  700. // the USN log.
  701. //
  702. USN usnStart = 0;
  703. //
  704. // A full scan is executed after a volume has been reformatted.
  705. // We first wipe all existing data (which will have incorrect
  706. // fileid <--> wid mappings) and then do the scan.
  707. //
  708. if ( pScanInfo->LokIsInFullUsnScan() )
  709. {
  710. _cicat.RemovePathsFromCiCat( pScanInfo->GetPath(),
  711. eUsnsArray );
  712. if ( !_fAbort && !_fUpdatesDisabled )
  713. {
  714. SCODE sc = pCiManager->FlushUpdates();
  715. if ( FAILED(sc) )
  716. THROW( CException( sc ) );
  717. }
  718. }
  719. else
  720. usnStart = pScanInfo->UsnStart();
  721. ciDebugOut(( DEB_ITRACE, "scan usnstart: %#I64x\n",
  722. pScanInfo->UsnStart() ));
  723. //
  724. // Use the end of the usn log or the last usn processed by
  725. // CI for this volume if available.
  726. //
  727. USN usnMax = FindCurrentMaxUsn( pScanInfo->GetPath() );
  728. {
  729. ciDebugOut(( DEB_ITRACE,
  730. "usn scan looking for better maxusn than %#I64x\n",
  731. usnMax ));
  732. for ( CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
  733. !_usnVolumesToMonitor.AtEnd( usnVolIter );
  734. _usnVolumesToMonitor.Advance( usnVolIter ) )
  735. {
  736. if ( usnVolIter->VolumeId() == pScanInfo->VolumeId() )
  737. {
  738. ciDebugOut(( DEB_ITRACE,
  739. "usn scan using %#I64x instead of %#I64x\n",
  740. usnVolIter->MaxUsnRead(),
  741. usnMax ));
  742. usnMax = usnVolIter->MaxUsnRead();
  743. break;
  744. }
  745. }
  746. }
  747. pScanInfo->SetStartUsn( usnMax );
  748. CLowerFunnyPath lcaseFunnyPath;
  749. lcaseFunnyPath.SetPath( pScanInfo->GetPath() );
  750. CUsnTreeTraversal usnTreeTrav( _cicat,
  751. *this,
  752. *pCiManager,
  753. lcaseFunnyPath,
  754. pScanInfo->IsDoDeletions(),
  755. _fUpdatesDisabled,
  756. TRUE, // Process root
  757. pScanInfo->VolumeId(),
  758. usnStart,
  759. usnMax,
  760. pScanInfo->IsUserInitiated() );
  761. usnTreeTrav.EndProcessing();
  762. //
  763. // Flush updates, because we will be writing usnMax as the
  764. // restart usn for this scope, and we want to make sure that
  765. // all updates with usn of 0 are serialized before writing
  766. // the restart usn.
  767. //
  768. if ( !_fAbort && !_fUpdatesDisabled )
  769. {
  770. SCODE sc = pCiManager->FlushUpdates();
  771. if ( FAILED(sc) )
  772. THROW( CException( sc ) );
  773. _cicat.SetUsnTreeScanComplete( pScanInfo->GetPath(), usnMax );
  774. //
  775. // Make SetUsnTreeScanComplete info persistent by
  776. // serializing the scope table.
  777. //
  778. _cicat.ScheduleSerializeChanges();
  779. pScanInfo->LokSetDone();
  780. }
  781. }
  782. }
  783. CATCH( CException, e)
  784. {
  785. ciDebugOut(( DEB_ERROR, "Exception 0x%x caught in CUsnMgr::DoUsnScans\n", e.GetErrorCode() ));
  786. status = e.GetErrorCode();
  787. }
  788. END_CATCH
  789. if ( status != STATUS_SUCCESS )
  790. {
  791. _cicat.HandleError( status );
  792. break;
  793. }
  794. }
  795. //
  796. // Requeue failed usn scans
  797. //
  798. while ( _usnScansInProgress.Count() > 0 )
  799. {
  800. XPtr<CCiScanInfo> xScanInfo( _usnScansInProgress.RemoveLast() );
  801. if ( _fUpdatesDisabled )
  802. {
  803. continue;
  804. }
  805. else if ( xScanInfo->LokIsInFinalState() )
  806. {
  807. if ( xScanInfo->LokIsDelScope() )
  808. {
  809. CLock lock( _mutex );
  810. LokRemoveScopeFromUsnMonitoring( xScanInfo );
  811. //
  812. // All processing for a delete scope has been completed
  813. //
  814. CCiScanInfo *pScanInfo = xScanInfo.Acquire();
  815. delete pScanInfo;
  816. }
  817. else
  818. {
  819. //
  820. // Start monitoring for changes on this scope
  821. //
  822. CLock lock( _mutex );
  823. LokAddScopeForUsnMonitoring( xScanInfo );
  824. }
  825. }
  826. else
  827. {
  828. //
  829. // We couldn't complete the scan for this, and so refile
  830. //
  831. xScanInfo->SetStartUsn( 0 );
  832. xScanInfo->IncrementRetries();
  833. xScanInfo->SetDoDeletions();
  834. ScanScope( xScanInfo, TRUE );
  835. }
  836. }
  837. } //DoUsnScans
  838. //+---------------------------------------------------------------------------
  839. //
  840. // Member: CUsnMgr::LokAddScopeForUsnMonitoring
  841. //
  842. // Synopsis: Registers a scope for usn notifications
  843. //
  844. // History: 07-May-97 SitaramR Created
  845. //
  846. //----------------------------------------------------------------------------
  847. void CUsnMgr::LokAddScopeForUsnMonitoring( XPtr<CCiScanInfo> & xScanInfo )
  848. {
  849. WCHAR const *pwszPath = xScanInfo->GetPath();
  850. TRY
  851. {
  852. //
  853. // Check that it is not a remote path
  854. //
  855. Win4Assert( pwszPath[0] != L'\\' );
  856. for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
  857. !_usnVolumesToMonitor.AtEnd(usnVolIter);
  858. _usnVolumesToMonitor.Advance(usnVolIter) )
  859. {
  860. if ( usnVolIter->DriveLetter() == pwszPath[0] )
  861. {
  862. //
  863. // Add the scope to the list of scopes being monitored
  864. // on this drive, if it is not already being monitored.
  865. //
  866. CScanInfoList & usnScopeList = usnVolIter->GetUsnScopesList();
  867. for ( CFwdScanInfoIter scanIter1(usnScopeList);
  868. !usnScopeList.AtEnd(scanIter1);
  869. usnScopeList.Advance(scanIter1) )
  870. {
  871. if ( AreIdenticalPaths( scanIter1->GetPath(), xScanInfo->GetPath() ) )
  872. return;
  873. CScopeMatch superScope( scanIter1->GetPath(), wcslen( scanIter1->GetPath() ) );
  874. if ( superScope.IsInScope( xScanInfo->GetPath(), wcslen( xScanInfo->GetPath() ) ) )
  875. return;
  876. }
  877. //
  878. // Remove subscopes as an optimization
  879. //
  880. CScopeMatch superScope( xScanInfo->GetPath(), wcslen( xScanInfo->GetPath() ) );
  881. CFwdScanInfoIter scanIter2(usnScopeList);
  882. while ( !usnScopeList.AtEnd(scanIter2) )
  883. {
  884. CCiScanInfo *pScanInfo = scanIter2.GetEntry();
  885. usnScopeList.Advance(scanIter2);
  886. if ( superScope.IsInScope( pScanInfo->GetPath(), wcslen( pScanInfo->GetPath() ) ) )
  887. {
  888. usnScopeList.RemoveFromList( pScanInfo );
  889. delete pScanInfo;
  890. }
  891. }
  892. if ( xScanInfo->UsnStart() < usnVolIter->MaxUsnRead() )
  893. {
  894. usnVolIter->SetMaxUsnRead( xScanInfo->UsnStart() );
  895. //
  896. // Cancel pending fsctl (if any) to pick up earlier usn
  897. // notifications
  898. //
  899. usnVolIter->CancelFsctl();
  900. }
  901. usnScopeList.Push( xScanInfo.Acquire() );
  902. return;
  903. }
  904. }
  905. CUsnVolume *pUsnVolume = new CUsnVolume( pwszPath[0], xScanInfo->VolumeId() );
  906. pUsnVolume->SetMaxUsnRead( xScanInfo->UsnStart() );
  907. _usnVolumesToMonitor.Push( pUsnVolume );
  908. CScanInfoList &usnScopeList = pUsnVolume->GetUsnScopesList();
  909. usnScopeList.Push( xScanInfo.Acquire() );
  910. }
  911. CATCH( CException, e )
  912. {
  913. ciDebugOut(( DEB_ERROR, "Error 0x%x trying to monitor USN scope %wc.\n", e.GetErrorCode(), xScanInfo->VolumeId() ));
  914. HandleError( pwszPath[0], e.GetErrorCode() );
  915. }
  916. END_CATCH
  917. } //LokAddScopeForUsnMonitoring
  918. //+---------------------------------------------------------------------------
  919. //
  920. // Member: CUsnMgr::LokRemoveScopeFromUsnMonitoring
  921. //
  922. // Synopsis: Deregisters a scope from usn notifications
  923. //
  924. // History: 07-May-97 SitaramR Created
  925. //
  926. //----------------------------------------------------------------------------
  927. void CUsnMgr::LokRemoveScopeFromUsnMonitoring( XPtr<CCiScanInfo> & xScanInfo )
  928. {
  929. WCHAR const *pwszPath = xScanInfo->GetPath();
  930. //
  931. // Check that it is not a remote path
  932. //
  933. Win4Assert( pwszPath[0] != L'\\' );
  934. for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
  935. !_usnVolumesToMonitor.AtEnd(usnVolIter);
  936. _usnVolumesToMonitor.Advance(usnVolIter) )
  937. {
  938. if ( usnVolIter->DriveLetter() == pwszPath[0] )
  939. {
  940. CScanInfoList & usnScopeList = usnVolIter->GetUsnScopesList();
  941. CFwdScanInfoIter scanInfoIter(usnScopeList);
  942. while ( !usnScopeList.AtEnd(scanInfoIter) )
  943. {
  944. CCiScanInfo *pScanInfo = scanInfoIter.GetEntry();
  945. usnScopeList.Advance(scanInfoIter);
  946. if ( AreIdenticalPaths( pScanInfo->GetPath(), xScanInfo->GetPath() ) )
  947. {
  948. usnScopeList.RemoveFromList( pScanInfo );
  949. delete pScanInfo;
  950. break;
  951. }
  952. }
  953. //
  954. // If no more scopes on this usn volume then remove the entry from the
  955. // list of volumes being monitored.
  956. //
  957. if ( usnScopeList.Count() == 0 )
  958. {
  959. CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
  960. _usnVolumesToMonitor.RemoveFromList( pUsnVolume );
  961. delete pUsnVolume;
  962. break;
  963. }
  964. }
  965. }
  966. } //LokRemoveScopeFromUsnMonitoring
  967. //+---------------------------------------------------------------------------
  968. //
  969. // Member: CUsnMgr::ProcessNotifications
  970. //
  971. // Synopsis: Wait for usn notifications, and then process them
  972. //
  973. // History: 07-May-97 SitaramR Created
  974. //
  975. //----------------------------------------------------------------------------
  976. void CUsnMgr::ProcessUsnNotifications()
  977. {
  978. //
  979. // No need for a lock because only this thread modifies _usnVolumesToMonitor
  980. //
  981. for (;;)
  982. {
  983. //
  984. // Loop by waiting for usn notifications and processing them.
  985. // Loop returns when _evtUsn is signalled.
  986. //
  987. BOOL fWait; // TRUE --> Every volume is now waiting. (assigned below)
  988. BOOL fLowResource;
  989. do
  990. {
  991. _waitForMultObj.ResetCount();
  992. //
  993. // _evtUsn is the first handle, and so it gets priority if more
  994. // than one event is signalled.
  995. //
  996. _waitForMultObj.AddEvent( _evtUsn.GetHandle() );
  997. CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
  998. //
  999. // Are we in a high i/o state? If so, don't read the USN Journal
  1000. //
  1001. fLowResource = IsLowResource();
  1002. fWait = TRUE;
  1003. while ( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
  1004. {
  1005. if ( _fAbort || _fUpdatesDisabled )
  1006. return;
  1007. CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
  1008. _usnVolumesToMonitor.Advance(usnVolIter);
  1009. if ( !pUsnVolume->IsOnline() )
  1010. continue;
  1011. ciDebugOut(( DEB_ITRACE, "Drive %wc: %u%% read\n", pUsnVolume->DriveLetter(), pUsnVolume->PercentRead() ));
  1012. if ( fLowResource && pUsnVolume->PercentRead() >= USN_LOG_DANGER_THRESHOLD )
  1013. continue;
  1014. //
  1015. // Does this volume have something to do now?
  1016. //
  1017. if ( pUsnVolume->FFsctlPending() && WAIT_TIMEOUT != pUsnVolume->WaitFsctl( 0 ) )
  1018. pUsnVolume->ResetFsctlPending();
  1019. if ( pUsnVolume->FFsctlPending() )
  1020. _waitForMultObj.AddEvent( pUsnVolume->GetFsctlEvent() );
  1021. else
  1022. {
  1023. NTSTATUS status = ProcessUsnNotificationsFromVolume( pUsnVolume );
  1024. switch( status )
  1025. {
  1026. case STATUS_PENDING:
  1027. pUsnVolume->SetFsctlPending();
  1028. _waitForMultObj.AddEvent( pUsnVolume->GetFsctlEvent() );
  1029. break;
  1030. case STATUS_JOURNAL_ENTRY_DELETED:
  1031. break;
  1032. case STATUS_SUCCESS:
  1033. fWait = FALSE; // More to do, processing bailed to enable round-robin
  1034. break;
  1035. default:
  1036. Win4Assert( !NT_SUCCESS( status ) );
  1037. HandleError( pUsnVolume, status );
  1038. break;
  1039. }
  1040. }
  1041. }
  1042. } while( !fWait );
  1043. if ( _fAbort )
  1044. return;
  1045. //
  1046. // Wait for evtUsn to be signaled or for an usn notification
  1047. //
  1048. DWORD dwTimeout = _cicat.GetRegParams()->GetUsnReadTimeout() * 1000;
  1049. //
  1050. // But only wait forever if we're actually watching notifications...
  1051. //
  1052. if ( 0 == dwTimeout )
  1053. {
  1054. if ( fLowResource )
  1055. dwTimeout = 60 * 1000;
  1056. else
  1057. dwTimeout = INFINITE;
  1058. }
  1059. //
  1060. // If we're in a low resource situation, this is a good place to flush
  1061. // the working set.
  1062. //
  1063. if ( fLowResource && _cicat.GetRegParams()->GetMinimizeWorkingSet() )
  1064. SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 );
  1065. _fWaitingForUpdates = TRUE;
  1066. DWORD dwIndex = _waitForMultObj.Wait( dwTimeout );
  1067. _fWaitingForUpdates = FALSE;
  1068. if ( _fAbort || _fUpdatesDisabled )
  1069. return;
  1070. if ( WAIT_FAILED == dwIndex )
  1071. {
  1072. Win4Assert( !"Waitformultipleobjects failed" );
  1073. ciDebugOut(( DEB_ERROR, "WaitForMultipleObjects failed, 0x%x", GetLastError() ));
  1074. THROW( CException( ) );
  1075. }
  1076. if ( WAIT_OBJECT_0 == dwIndex )
  1077. {
  1078. //
  1079. // _evtUsn has been signaled -- cancel pending requests
  1080. //
  1081. CUsnVolumeIter usnVolIter( _usnVolumesToMonitor );
  1082. while ( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
  1083. {
  1084. CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
  1085. _usnVolumesToMonitor.Advance(usnVolIter);
  1086. pUsnVolume->CancelFsctl();
  1087. }
  1088. return;
  1089. }
  1090. else if ( WAIT_TIMEOUT != dwIndex )
  1091. {
  1092. //
  1093. // Process usn notifications from the volume that was signaled.
  1094. // GetIth does a linear lookup, it can be optimized to index
  1095. // directly into an array of pUsnVolumes.
  1096. //
  1097. unsigned i = dwIndex - WAIT_OBJECT_0 - 1;
  1098. CUsnVolume *pUsnVolume = _usnVolumesToMonitor.GetIth( i );
  1099. //
  1100. // We know the volume is at least the Ith volume, but it may be
  1101. // farther if some volumes were skipped.
  1102. //
  1103. i++; // Account for control event
  1104. while ( pUsnVolume->GetFsctlEvent() != _waitForMultObj.Get(i) )
  1105. pUsnVolume = (CUsnVolume *)pUsnVolume->Next();
  1106. pUsnVolume->ResetFsctlPending();
  1107. ciDebugOut(( DEB_ITRACE, "PENDING COMPLETED #1 (0x%x)\n", pUsnVolume ));
  1108. if ( !fLowResource || pUsnVolume->PercentRead() < USN_LOG_DANGER_THRESHOLD )
  1109. ProcessUsnLogRecords( pUsnVolume );
  1110. if ( _fAbort || _fUpdatesDisabled )
  1111. return;
  1112. }
  1113. if ( 0 != dwTimeout && !fLowResource )
  1114. {
  1115. //
  1116. // Check to see if there is anything that's sat in the buffer too long.
  1117. //
  1118. LONGLONG ftExpired;
  1119. GetSystemTimeAsFileTime( (FILETIME *)&ftExpired );
  1120. ftExpired -= _cicat.GetRegParams()->GetUsnReadTimeout() * 10000i64;
  1121. CUsnVolumeIter usnVolIter(_usnVolumesToMonitor);
  1122. while( !_usnVolumesToMonitor.AtEnd(usnVolIter) )
  1123. {
  1124. if ( _fAbort || _fUpdatesDisabled )
  1125. break;
  1126. CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
  1127. _usnVolumesToMonitor.Advance(usnVolIter);
  1128. if ( !pUsnVolume->IsOnline() )
  1129. continue;
  1130. if ( pUsnVolume->FFsctlPending() && pUsnVolume->PendingTime() <= ftExpired )
  1131. {
  1132. pUsnVolume->CancelFsctl();
  1133. ciDebugOut(( DEB_ITRACE, "PENDING CANCELLED #1 (0x%x)\n", pUsnVolume ));
  1134. NTSTATUS status = ProcessUsnNotificationsFromVolume( pUsnVolume, TRUE ); // TRUE --> Immediate
  1135. switch( status )
  1136. {
  1137. case STATUS_PENDING:
  1138. pUsnVolume->SetFsctlPending();
  1139. break;
  1140. case STATUS_JOURNAL_ENTRY_DELETED:
  1141. break;
  1142. case STATUS_SUCCESS:
  1143. fWait = FALSE; // More to do, processing bailed to enable round-robin
  1144. break;
  1145. default:
  1146. Win4Assert( !NT_SUCCESS( status ) );
  1147. HandleError( pUsnVolume, status );
  1148. break;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. }
  1154. } //ProcessUsnNotifications
  1155. //+---------------------------------------------------------------------------
  1156. //
  1157. // Member: CUsnMgr::ProcessUsnNotificationsFromVolume
  1158. //
  1159. // Synopsis: Process usn notifications from given volume
  1160. //
  1161. // Arguments: [pUsnVolume] -- Usn volume
  1162. // [fImmediate] -- If TRUE, look for *any* new data in log,
  1163. // no matter how little.
  1164. // [fWait] -- If TRUE, wait for data if fImmediate is TRUE
  1165. // and no data is available.
  1166. //
  1167. //
  1168. // Returns: STATUS_PENDING when there are no more usn notifications to be
  1169. // processed, or STATUS_JOURNAL_ENTRY_DELETED when usn records
  1170. // have been removed from log due to shrink from front.
  1171. //
  1172. // History: 05-Jul-97 SitaramR Created
  1173. //
  1174. //----------------------------------------------------------------------------
  1175. NTSTATUS CUsnMgr::ProcessUsnNotificationsFromVolume( CUsnVolume * pUsnVolume,
  1176. BOOL fImmediate,
  1177. BOOL fWait )
  1178. {
  1179. Win4Assert( pUsnVolume->IsOnline() );
  1180. //
  1181. // No need for a lock because only this thread modifies _usnVolumesToMonitor
  1182. //
  1183. Win4Assert( !pUsnVolume->FFsctlPending() );
  1184. NTSTATUS status;
  1185. unsigned cRetries = 0; // Unsuccessful read attempts
  1186. unsigned cTries = 0; // Successful read attempts
  1187. READ_USN_JOURNAL_DATA usnReadData;
  1188. usnReadData.UsnJournalID = pUsnVolume->JournalId();
  1189. usnReadData.StartUsn = pUsnVolume->MaxUsnRead();
  1190. usnReadData.ReasonMask = ~(USN_REASON_RENAME_OLD_NAME | USN_REASON_COMPRESSION_CHANGE);
  1191. usnReadData.ReturnOnlyOnClose = TRUE;
  1192. usnReadData.Timeout = 0;
  1193. do
  1194. {
  1195. if ( _fAbort || _fUpdatesDisabled )
  1196. {
  1197. //
  1198. // Doesn't matter what status we return because all notifications
  1199. // will be turned off soon.
  1200. //
  1201. return STATUS_TOO_LATE;
  1202. }
  1203. //
  1204. // In immediate mode, read *any* data, else read as requested by client.
  1205. //
  1206. if ( fImmediate )
  1207. usnReadData.BytesToWaitFor = 1;
  1208. else
  1209. usnReadData.BytesToWaitFor = _cicat.GetRegParams()->GetUsnReadMinSize();
  1210. #if CIDBG == 1
  1211. RtlFillMemory( pUsnVolume->GetBuffer(), pUsnVolume->GetBufferSize(), 0x11 );
  1212. #endif
  1213. ciDebugOut(( DEB_ITRACE, "READ #1 (0x%x)\n", pUsnVolume ));
  1214. Win4Assert( !pUsnVolume->FFsctlPending() );
  1215. ciDebugOut(( DEB_ITRACE, "reading usns from %#I64x\n", usnReadData.StartUsn ));
  1216. status = NtFsControlFile( pUsnVolume->VolumeHandle(),
  1217. pUsnVolume->GetFsctlEvent(),
  1218. NULL,
  1219. NULL,
  1220. pUsnVolume->IoStatusBlock(),
  1221. FSCTL_READ_USN_JOURNAL,
  1222. &usnReadData,
  1223. sizeof(usnReadData),
  1224. pUsnVolume->GetBuffer(),
  1225. pUsnVolume->GetBufferSize() );
  1226. if ( fImmediate )
  1227. {
  1228. if ( fWait )
  1229. fImmediate = FALSE;
  1230. //
  1231. // STATUS_PENDING --> nothing in the log. Go back to regular waits.
  1232. //
  1233. if ( STATUS_PENDING == status )
  1234. {
  1235. ciDebugOut(( DEB_ITRACE, "PENDING CANCELLED #2 (0x%x)\n", pUsnVolume ));
  1236. pUsnVolume->SetFsctlPending();
  1237. pUsnVolume->CancelFsctl();
  1238. // If no journal entries are available and we're not supposed
  1239. // to wait, return now.
  1240. if ( !fWait )
  1241. return STATUS_SUCCESS;
  1242. continue;
  1243. }
  1244. }
  1245. if ( STATUS_PENDING == status )
  1246. break;
  1247. if ( NT_SUCCESS( status ) )
  1248. status = pUsnVolume->IoStatusBlock()->Status;
  1249. if ( NT_SUCCESS( status ) )
  1250. {
  1251. ciDebugOut(( DEB_ITRACE, "IMMEDIATE COMPLETED #1 (0x%x)\n", pUsnVolume ));
  1252. ProcessUsnLogRecords( pUsnVolume );
  1253. usnReadData.StartUsn = pUsnVolume->MaxUsnRead();
  1254. Win4Assert( usnReadData.ReasonMask == ~(USN_REASON_RENAME_OLD_NAME | USN_REASON_COMPRESSION_CHANGE) );
  1255. Win4Assert( usnReadData.ReturnOnlyOnClose == TRUE );
  1256. //
  1257. // Don't read a single volume too long...
  1258. //
  1259. cTries++;
  1260. if ( cTries > 5 )
  1261. {
  1262. ciDebugOut(( DEB_ITRACE, "Stopping read on drive %wc: to support round-robin\n", pUsnVolume->DriveLetter() ));
  1263. break;
  1264. }
  1265. else
  1266. continue;
  1267. }
  1268. else if ( STATUS_JOURNAL_ENTRY_DELETED == status )
  1269. {
  1270. ciDebugOut(( DEB_ERROR, "STATUS_JOURNAL_ENTRY_DELETED #1 fWait %d, (drive %wc:)\n",
  1271. fWait, pUsnVolume->DriveLetter() ));
  1272. // Already doing a scan due to missed notifications.
  1273. if ( !fWait )
  1274. return STATUS_JOURNAL_ENTRY_DELETED;
  1275. //
  1276. // STATUS_JOURNAL_ENTRY_DELETED is returned when usn records have
  1277. // been deallocated from the usn journal. This means that we have to
  1278. // do a scan before continuing usn monitoring. Even if some other
  1279. // error code has been returned, the simplest recovery is to restart
  1280. // monitoring.
  1281. //
  1282. XBatchUsnProcessing xBatchUsns( *this );
  1283. CScanInfoList & usnScopeList = pUsnVolume->GetUsnScopesList();
  1284. while ( usnScopeList.Count() > 0 )
  1285. {
  1286. XPtr<CCiScanInfo> xScanInfo( usnScopeList.Pop() );
  1287. xScanInfo->SetStartState();
  1288. xScanInfo->SetScan();
  1289. ciDebugOut(( DEB_ERROR, "journal_entry_deleted, filing a scan for %ws\n",
  1290. xScanInfo->GetPath() ));
  1291. xScanInfo->SetStartUsn( pUsnVolume->MaxUsnRead() );
  1292. xScanInfo->SetDoDeletions();
  1293. _cicat.InitUsnTreeScan( xScanInfo->GetPath() );
  1294. ScanScope( xScanInfo, TRUE );
  1295. }
  1296. _usnVolumesToMonitor.RemoveFromList( pUsnVolume );
  1297. delete pUsnVolume;
  1298. return STATUS_JOURNAL_ENTRY_DELETED;
  1299. }
  1300. else
  1301. {
  1302. ciDebugOut(( DEB_WARN, "Usn read fsctl returned 0x%x\n", status ));
  1303. cRetries++;
  1304. if ( cRetries > 10 )
  1305. {
  1306. ciDebugOut(( DEB_WARN, "Giving up on USN read.\n" ));
  1307. break;
  1308. }
  1309. // the fsctl failed -- so try again
  1310. continue;
  1311. }
  1312. } while ( status != STATUS_PENDING );
  1313. return status;
  1314. } //ProcessUsnNotificationsFromVolume
  1315. //+---------------------------------------------------------------------------
  1316. //
  1317. // Member: CUsnMgr::ProcessUsnLogRecords
  1318. //
  1319. // Synopsis: Process usn notifications from usn records in buffer
  1320. //
  1321. // Arguments: [pUsnVolume] -- Usn volume that contains the buffer
  1322. //
  1323. // History: 05-Jul-97 SitaramR Created
  1324. //
  1325. //----------------------------------------------------------------------------
  1326. void CUsnMgr::ProcessUsnLogRecords( CUsnVolume *pUsnVolume )
  1327. {
  1328. Win4Assert( pUsnVolume->IsOnline() );
  1329. //
  1330. // No need for a lock because only this thread modifies _usnVolumesToMonitor
  1331. //
  1332. USN usnNextStart;
  1333. USN_RECORD * pUsnRec = 0;
  1334. ciDebugOut(( DEB_ITRACE, "process usns, Status 0x%x, Bytes %d\n",
  1335. pUsnVolume->IoStatusBlock()->Status,
  1336. pUsnVolume->IoStatusBlock()->Information ));
  1337. if ( !NT_SUCCESS( pUsnVolume->IoStatusBlock()->Status ) )
  1338. {
  1339. //
  1340. // If we cancelled the notification request (usually because
  1341. // we're going into a mode where we wait for a lot of bytes instead
  1342. // of just 1 byte), just ignore processing the request.
  1343. //
  1344. if ( STATUS_CANCELLED != pUsnVolume->IoStatusBlock()->Status )
  1345. {
  1346. ciDebugOut(( DEB_WARN, "Error 0x%x reading USN Journal\n", pUsnVolume->IoStatusBlock()->Status ));
  1347. Win4Assert( STATUS_INVALID_PARAMETER != pUsnVolume->IoStatusBlock()->Status );
  1348. }
  1349. return;
  1350. }
  1351. DWORD dwByteCount = (DWORD)pUsnVolume->IoStatusBlock()->Information;
  1352. if ( dwByteCount != 0 )
  1353. {
  1354. usnNextStart = *(USN *)pUsnVolume->GetBuffer();
  1355. pUsnRec = (USN_RECORD *)((PCHAR)pUsnVolume->GetBuffer() + sizeof(USN));
  1356. dwByteCount -= sizeof(USN);
  1357. }
  1358. if ( 0 == pUsnRec )
  1359. return;
  1360. ICiManager *pCiManager = _cicat.CiManager();
  1361. Win4Assert( pCiManager );
  1362. while ( dwByteCount != 0 )
  1363. {
  1364. if ( _fAbort || _fUpdatesDisabled )
  1365. return;
  1366. //
  1367. // Check that usn's from journal are greater than the start usn
  1368. //
  1369. #if CIDBG == 1
  1370. if ( pUsnRec->Usn < pUsnVolume->MaxUsnRead() )
  1371. {
  1372. ciDebugOut(( DEB_ERROR,
  1373. "volume %wc pUsnRec = 0x%x, USN = %#I64x, Max USN = %#I64x\n",
  1374. pUsnVolume->VolumeId(), pUsnRec, pUsnRec->Usn,
  1375. pUsnVolume->MaxUsnRead() ));
  1376. Win4Assert( pUsnRec->Usn >= pUsnVolume->MaxUsnRead() );
  1377. Sleep( 30 * 1000 );
  1378. }
  1379. Win4Assert( pUsnRec->Usn >= pUsnVolume->MaxUsnRead() );
  1380. if ( pUsnRec->RecordLength > 10000 )
  1381. {
  1382. ciDebugOut(( DEB_ERROR, "pUsnRec = 0x%x, RecordLength = %u\n", pUsnRec, pUsnRec->RecordLength ));
  1383. }
  1384. Win4Assert( pUsnRec->RecordLength <= 10000 );
  1385. #endif
  1386. if ( 0 == (pUsnRec->SourceInfo & (USN_SOURCE_AUXILIARY_DATA | USN_SOURCE_DATA_MANAGEMENT)) &&
  1387. ( 0 == (pUsnRec->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) ||
  1388. 0 != (pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE) ) )
  1389. {
  1390. //
  1391. // Indexable bit==0 means index the file, because the bit is set to 0
  1392. // by default on an upgrade from Ntfs 4.0 to Ntfs 5.0. We want the
  1393. // the default behavior to be index-all-files to ensure backward
  1394. // compatibility.
  1395. //
  1396. if ( pUsnRec->Reason & USN_REASON_RENAME_NEW_NAME )
  1397. {
  1398. CLowerFunnyPath lcaseFunnyOldPath;
  1399. BOOL fOldPathInScope;
  1400. _cicat.FileIdToPath( pUsnRec->FileReferenceNumber,
  1401. pUsnVolume,
  1402. lcaseFunnyOldPath,
  1403. fOldPathInScope );
  1404. CLowerFunnyPath lcaseFunnyNewPath;
  1405. BOOL fNewPathInScope;
  1406. WORKID widParent;
  1407. #if CIDBG == 1
  1408. widParent = widUnused;
  1409. #endif
  1410. //
  1411. // Try hard to find a parent directory, since it may
  1412. // not be in the index if it is marked as not-indexed.
  1413. //
  1414. _cicat.UsnRecordToPathUsingParentId( pUsnRec,
  1415. pUsnVolume,
  1416. lcaseFunnyNewPath,
  1417. fNewPathInScope,
  1418. widParent,
  1419. TRUE );
  1420. Win4Assert( widUnused != widParent );
  1421. if ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  1422. {
  1423. //
  1424. // Directory rename or add or delete. Directory operations are
  1425. // (re)tried a certain number of times, after which a failure
  1426. // event is logged.
  1427. //
  1428. const MAX_RETRIES = 5;
  1429. XGrowable<WCHAR> xFileName;
  1430. for (unsigned i=0; i<MAX_RETRIES; i++)
  1431. {
  1432. TRY
  1433. {
  1434. if ( fOldPathInScope )
  1435. {
  1436. if ( fNewPathInScope )
  1437. {
  1438. ciDebugOut(( DEB_USN, "Renaming directory %ws to %ws\n", lcaseFunnyOldPath.GetPath(), lcaseFunnyNewPath.GetPath() ));
  1439. CRenameDir( _cicat,
  1440. lcaseFunnyOldPath,
  1441. lcaseFunnyNewPath,
  1442. _fUpdatesDisabled,
  1443. pUsnVolume->VolumeId() );
  1444. _cicat.Update( lcaseFunnyNewPath,
  1445. pUsnRec->FileReferenceNumber,
  1446. widParent,
  1447. pUsnRec->Usn,
  1448. pUsnVolume,
  1449. FALSE );
  1450. break;
  1451. }
  1452. else
  1453. {
  1454. CLowcaseBuf lcaseOldPath( lcaseFunnyOldPath.GetActualPath() );
  1455. lcaseOldPath.AppendBackSlash();
  1456. ciDebugOut(( DEB_USN, "Removing directory %ws\n", lcaseOldPath.Get() ));
  1457. _cicat.RemovePathsFromCiCat( lcaseOldPath.Get(), eUsnsArray );
  1458. if ( _fAbort || _fUpdatesDisabled )
  1459. {
  1460. return;
  1461. }
  1462. SCODE sc = pCiManager->FlushUpdates();
  1463. if ( FAILED(sc) )
  1464. THROW( CException( sc ) );
  1465. break;
  1466. }
  1467. }
  1468. else
  1469. {
  1470. if ( fNewPathInScope )
  1471. {
  1472. lcaseFunnyNewPath.AppendBackSlash();
  1473. ciDebugOut(( DEB_USN, "Adding directory %ws\n", lcaseFunnyNewPath.GetPath() ));
  1474. _fDoingRenameTraverse = TRUE;
  1475. CUsnTreeTraversal usnTreeTrav( _cicat,
  1476. *this,
  1477. *pCiManager,
  1478. lcaseFunnyNewPath,
  1479. FALSE, // No deletions
  1480. _fUpdatesDisabled,
  1481. TRUE, // Process root
  1482. pUsnVolume->VolumeId() );
  1483. usnTreeTrav.EndProcessing();
  1484. _fDoingRenameTraverse = FALSE;
  1485. if ( _fAbort || _fUpdatesDisabled )
  1486. return;
  1487. SCODE sc = pCiManager->FlushUpdates();
  1488. if ( FAILED(sc) )
  1489. THROW( CException( sc ) );
  1490. break;
  1491. }
  1492. else
  1493. {
  1494. //
  1495. // Both old and new paths are out of scope. We *still* have to
  1496. // determine whether this rename caused a top-level indexed scope
  1497. // to either exist or cease to exist.
  1498. //
  1499. CheckTopLevelChange( pUsnVolume, pUsnRec->FileReferenceNumber );
  1500. break;
  1501. }
  1502. }
  1503. }
  1504. CATCH( CException, e )
  1505. {
  1506. _fDoingRenameTraverse = FALSE;
  1507. #if CIDBG==1
  1508. //
  1509. // pUsnRec->FileName is not null terminated, so copy
  1510. // and null terminate.
  1511. //
  1512. unsigned cbFileNameLen = pUsnRec->FileNameLength;
  1513. xFileName.SetSizeInBytes( cbFileNameLen + 2 );
  1514. RtlCopyMemory( xFileName.Get(),
  1515. (WCHAR *)(((BYTE *)pUsnRec) + pUsnRec->FileNameOffset),
  1516. cbFileNameLen );
  1517. xFileName[cbFileNameLen/sizeof(WCHAR)] = 0;
  1518. ciDebugOut(( DEB_ERROR,
  1519. "Exception 0x%x while doing directory rename/add/delete of %ws\n",
  1520. e.GetErrorCode(),
  1521. xFileName.Get() ));
  1522. #endif
  1523. }
  1524. END_CATCH
  1525. } // for
  1526. if ( i == MAX_RETRIES )
  1527. {
  1528. CEventLog eventLog( NULL, wcsCiEventSource );
  1529. CEventItem item( EVENTLOG_ERROR_TYPE,
  1530. CI_SERVICE_CATEGORY,
  1531. MSG_CI_CONTENTSCAN_FAILED,
  1532. 1 );
  1533. //
  1534. // pUsnRec->FileName is not null terminated, so copy
  1535. // and null terminate.
  1536. //
  1537. unsigned cbFileNameLen = pUsnRec->FileNameLength;
  1538. xFileName.SetSizeInBytes( cbFileNameLen + 2 );
  1539. RtlCopyMemory( xFileName.Get(),
  1540. (WCHAR *)(((BYTE *)pUsnRec) + pUsnRec->FileNameOffset),
  1541. cbFileNameLen );
  1542. xFileName[cbFileNameLen/sizeof(WCHAR)] = 0;
  1543. item.AddArg( xFileName.Get() );
  1544. eventLog.ReportEvent( item );
  1545. }
  1546. }
  1547. else // if reason & FILE_ATTRIBUTE_DIRECTORY
  1548. {
  1549. //
  1550. // File rename or add or delete
  1551. //
  1552. if ( fOldPathInScope )
  1553. {
  1554. if ( fNewPathInScope )
  1555. {
  1556. ciDebugOut(( DEB_USN, "Renaming file %ws to %ws\n",
  1557. lcaseFunnyOldPath.GetActualPath(), lcaseFunnyNewPath.GetActualPath() ));
  1558. _cicat.RenameFile( lcaseFunnyOldPath,
  1559. lcaseFunnyNewPath,
  1560. pUsnRec->FileAttributes,
  1561. pUsnVolume->VolumeId(),
  1562. pUsnRec->FileReferenceNumber,
  1563. widParent );
  1564. _cicat.Update( lcaseFunnyNewPath,
  1565. pUsnRec->FileReferenceNumber,
  1566. widParent,
  1567. pUsnRec->Usn,
  1568. pUsnVolume,
  1569. FALSE );
  1570. }
  1571. else
  1572. {
  1573. _cicat.Update( lcaseFunnyOldPath,
  1574. pUsnRec->FileReferenceNumber,
  1575. widParent,
  1576. pUsnRec->Usn,
  1577. pUsnVolume,
  1578. TRUE );
  1579. }
  1580. }
  1581. else
  1582. {
  1583. if ( fNewPathInScope )
  1584. {
  1585. _cicat.Update( lcaseFunnyNewPath,
  1586. pUsnRec->FileReferenceNumber,
  1587. widParent,
  1588. pUsnRec->Usn,
  1589. pUsnVolume,
  1590. FALSE );
  1591. }
  1592. else
  1593. {
  1594. //
  1595. // Both old and new paths are out of scope, so do nothing
  1596. //
  1597. }
  1598. }
  1599. }
  1600. }
  1601. else
  1602. {
  1603. Win4Assert( pUsnRec->Reason & USN_REASON_CLOSE );
  1604. if ( 0 != (pUsnRec->Reason & ~(USN_REASON_CLOSE | USN_REASON_COMPRESSION_CHANGE | USN_REASON_RENAME_OLD_NAME ) ) )
  1605. {
  1606. CLowerFunnyPath lcaseFunnyPath;
  1607. BOOL fPathInScope = FALSE;
  1608. WORKID widParent = widInvalid;
  1609. CReleasableLock lock( _cicat.GetMutex() );
  1610. WORKID wid = _cicat.FileIdToWorkId( pUsnRec->FileReferenceNumber,
  1611. pUsnVolume->VolumeId() );
  1612. if ( 0 != ( pUsnRec->Reason & USN_REASON_FILE_DELETE ) ||
  1613. 0 != ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ) )
  1614. {
  1615. //
  1616. // File delete. Don't delete directories that go from
  1617. // indexed to non-indexed since there may be files
  1618. // below them that are indexed.
  1619. //
  1620. if ( ( widInvalid != wid ) )
  1621. {
  1622. CDocumentUpdateInfo docInfo( wid,
  1623. pUsnVolume->VolumeId(),
  1624. pUsnRec->Usn,
  1625. TRUE );
  1626. pCiManager->UpdateDocument( &docInfo );
  1627. if ( ! ( ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
  1628. ( pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE ) ) )
  1629. {
  1630. _cicat.LokMarkForDeletion( pUsnRec->FileReferenceNumber,
  1631. wid );
  1632. }
  1633. else
  1634. {
  1635. ciDebugOut(( DEB_ITRACE, "usn notify not deleting a ni directory\n" ));
  1636. }
  1637. }
  1638. }
  1639. else
  1640. {
  1641. //
  1642. // File create or modify
  1643. //
  1644. Win4Assert( pUsnRec->Reason & USN_REASON_CLOSE );
  1645. if ( widInvalid == wid )
  1646. {
  1647. _cicat.UsnRecordToPathUsingParentId( pUsnRec,
  1648. pUsnVolume,
  1649. lcaseFunnyPath,
  1650. fPathInScope,
  1651. widParent,
  1652. ( 0 != ( pUsnRec->Reason & USN_REASON_INDEXABLE_CHANGE ) ) );
  1653. if ( fPathInScope )
  1654. _cicat.Update( lcaseFunnyPath,
  1655. pUsnRec->FileReferenceNumber,
  1656. widParent,
  1657. pUsnRec->Usn,
  1658. pUsnVolume,
  1659. FALSE, // not a delete
  1660. &lock ); // guaranteed new file
  1661. else if ( pUsnRec->FileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  1662. {
  1663. //
  1664. // This might be a top-level directory. No parent id to
  1665. // look up, but still the root of the tree.
  1666. //
  1667. lock.Release();
  1668. CheckTopLevelChange( pUsnVolume, pUsnRec->FileReferenceNumber );
  1669. }
  1670. }
  1671. else
  1672. {
  1673. lock.Release();
  1674. CDocumentUpdateInfo docInfo( wid,
  1675. pUsnVolume->VolumeId(),
  1676. pUsnRec->Usn,
  1677. FALSE );
  1678. pCiManager->UpdateDocument( &docInfo );
  1679. }
  1680. } // if fPathInScope
  1681. } // if reason is one we care about
  1682. } // if-else reason & reason_new_name
  1683. } // pUsnRec->fileattr & file_attr_content_indexed
  1684. if ( pUsnRec->RecordLength <= dwByteCount )
  1685. {
  1686. dwByteCount -= pUsnRec->RecordLength;
  1687. //#if CIDBG == 1
  1688. #if 0
  1689. ULONG cb = pUsnRec->RecordLength;
  1690. RtlFillMemory( pUsnRec, cb, 0xEE );
  1691. pUsnRec = (USN_RECORD *) ((PCHAR) pUsnRec + cb );
  1692. #else
  1693. pUsnRec = (USN_RECORD *) ((PCHAR) pUsnRec + pUsnRec->RecordLength );
  1694. #endif
  1695. }
  1696. else
  1697. {
  1698. Win4Assert( !"Bogus dwByteCount" );
  1699. ciDebugOut(( DEB_ERROR,
  1700. "Usn read fsctl returned bogus dwByteCount, 0x%x\n",
  1701. dwByteCount ));
  1702. THROW( CException( STATUS_UNSUCCESSFUL ) );
  1703. }
  1704. }
  1705. pUsnVolume->SetMaxUsnRead( usnNextStart );
  1706. } //ProcessUsnLogRecords
  1707. //+---------------------------------------------------------------------------
  1708. //
  1709. // Member: CUsnMgr::FindCurrentMaxUsn
  1710. //
  1711. // Synopsis: Find the current max usn for the volume with pwcsScope
  1712. //
  1713. // Arguments: [pwcsScope] - Scope
  1714. //
  1715. // History: 07-May-97 SitaramR Created
  1716. //
  1717. //----------------------------------------------------------------------------
  1718. USN CUsnMgr::FindCurrentMaxUsn( WCHAR const * pwcsScope )
  1719. {
  1720. Win4Assert( pwcsScope );
  1721. WCHAR wszVolumePath[] = L"\\\\.\\a:";
  1722. wszVolumePath[4] = pwcsScope[0];
  1723. HANDLE hVolume = CreateFile( wszVolumePath,
  1724. GENERIC_READ | GENERIC_WRITE,
  1725. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1726. NULL,
  1727. OPEN_EXISTING,
  1728. 0,
  1729. NULL );
  1730. if ( hVolume == INVALID_HANDLE_VALUE )
  1731. THROW( CException( STATUS_UNSUCCESSFUL ) );
  1732. SWin32Handle xHandleVolume( hVolume );
  1733. IO_STATUS_BLOCK iosb;
  1734. USN_JOURNAL_DATA UsnJournalInfo;
  1735. NTSTATUS Status = NtFsControlFile( hVolume,
  1736. NULL,
  1737. NULL,
  1738. NULL,
  1739. &iosb,
  1740. FSCTL_QUERY_USN_JOURNAL,
  1741. 0,
  1742. 0,
  1743. &UsnJournalInfo,
  1744. sizeof(UsnJournalInfo) );
  1745. Win4Assert( STATUS_PENDING != Status );
  1746. if ( NT_SUCCESS(Status) )
  1747. Status = iosb.Status;
  1748. if ( NT_ERROR(Status) )
  1749. {
  1750. //
  1751. // Usn journal should have been created already
  1752. //
  1753. ciDebugOut(( DEB_ERROR,
  1754. "Usn read fsctl returned 0x%x\n",
  1755. Status ));
  1756. THROW( CException( Status ) );
  1757. }
  1758. return UsnJournalInfo.NextUsn;
  1759. } //FindCurrentMaxUsn
  1760. //+---------------------------------------------------------------------------
  1761. //
  1762. // Member: CUsnMgr::SetBatch
  1763. //
  1764. // Synopsis: Sets the batch flag, which means that the usn thread will not
  1765. // be signalled to start processing requests until the batch
  1766. // flag is cleared.
  1767. //
  1768. // History: 27-Jun-97 SitaramR Created
  1769. //
  1770. //----------------------------------------------------------------------------
  1771. void CUsnMgr::SetBatch()
  1772. {
  1773. CLock lock(_mutex);
  1774. _fBatch = TRUE;
  1775. }
  1776. //+---------------------------------------------------------------------------
  1777. //
  1778. // Member: CUsnMgr::ClearBatch
  1779. //
  1780. // Synopsis: Clears the batch processing flag and wakes up the usn
  1781. // thread and the accumulated scans processed.
  1782. //
  1783. // History: 27-Jun-97 SitaramR Created
  1784. //
  1785. //----------------------------------------------------------------------------
  1786. void CUsnMgr::ClearBatch()
  1787. {
  1788. CLock lock(_mutex);
  1789. _fBatch = FALSE;
  1790. _evtUsn.Set();
  1791. }
  1792. struct SVolumeUsn
  1793. {
  1794. VOLUMEID volumeId;
  1795. USN usn;
  1796. };
  1797. //+---------------------------------------------------------------------------
  1798. //
  1799. // Member: CUsnMgr::ProcessUsnLog, public
  1800. //
  1801. // Synopsis: Processes all records in the USN log for all volumes.
  1802. // This is so that we don't have to rescan when a scan
  1803. // completes
  1804. //
  1805. // Arguments: [fAbort] -- Bail if TRUE
  1806. // [volScan] -- Volume Id of volume current being scanned (will
  1807. // be added to volumes processed).
  1808. // [usnScan] -- USN on [volScan] at which monitoring will commence
  1809. // when scan is complete.
  1810. //
  1811. // History: 28-May-98 dlee Created
  1812. //
  1813. //----------------------------------------------------------------------------
  1814. void CUsnMgr::ProcessUsnLog( BOOL & fAbort, VOLUMEID volScan, USN & usnScan )
  1815. {
  1816. if ( _fDoingRenameTraverse )
  1817. return;
  1818. CDynArrayInPlace<SVolumeUsn> aVolumeUsns;
  1819. //
  1820. // Add the volume actively being scanned. No point in monitoring others with
  1821. // pending scans, since they will be scanned anyway.
  1822. //
  1823. aVolumeUsns[0].volumeId = volScan;
  1824. aVolumeUsns[0].usn = usnScan;
  1825. //
  1826. // Add all the volumes being monitored
  1827. //
  1828. { // Save stack, by overloading usnVolIter
  1829. for ( CUsnVolumeIter usnVolIter(_usnVolumesToMonitor); // with the one farther down the function...
  1830. !_usnVolumesToMonitor.AtEnd(usnVolIter);
  1831. _usnVolumesToMonitor.Advance(usnVolIter) )
  1832. {
  1833. Win4Assert( !usnVolIter->FFsctlPending() );
  1834. VOLUMEID volumeId = usnVolIter->VolumeId();
  1835. USN usn = usnVolIter->MaxUsnRead();
  1836. for ( unsigned i = 0; i < aVolumeUsns.Count(); i++ )
  1837. {
  1838. if ( aVolumeUsns[i].volumeId == volumeId )
  1839. {
  1840. aVolumeUsns[i].usn = __min( aVolumeUsns[i].usn, usn );
  1841. break;
  1842. }
  1843. }
  1844. if ( i == aVolumeUsns.Count() )
  1845. {
  1846. aVolumeUsns[ i ].volumeId = volumeId;
  1847. aVolumeUsns[ i ].usn = usn;
  1848. }
  1849. }
  1850. }
  1851. //
  1852. // Take this list, and create temporary volume objects.
  1853. //
  1854. CUsnVolumeList usnVolumesToProcess;
  1855. for ( unsigned i = 0; i < aVolumeUsns.Count(); i++ )
  1856. {
  1857. CUsnVolume * pusnVolume = new CUsnVolume( (WCHAR) aVolumeUsns[i].volumeId, aVolumeUsns[i].volumeId );
  1858. pusnVolume->SetMaxUsnRead( aVolumeUsns[i].usn );
  1859. usnVolumesToProcess.Push( pusnVolume );
  1860. }
  1861. //
  1862. // Process USNs for the volumes until they are all out of danger
  1863. //
  1864. BOOL fSomethingChanged = FALSE;
  1865. int priOriginal;
  1866. unsigned cInDanger = 0;
  1867. BOOL fFirst = TRUE;
  1868. do
  1869. {
  1870. cInDanger = 0;
  1871. for ( CUsnVolumeIter usnVolIter(usnVolumesToProcess);
  1872. !usnVolumesToProcess.AtEnd(usnVolIter);
  1873. usnVolumesToProcess.Advance(usnVolIter) )
  1874. {
  1875. CUsnVolume *pUsnVolume = usnVolIter.GetEntry();
  1876. // Process the usn notifications for the volume.
  1877. ULONG ulPct = pUsnVolume->PercentRead();
  1878. // If the journal is entirely empty, it's not in danger.
  1879. if ( !fFirst && ( 0 == ulPct ) && ( 0 == pUsnVolume->MaxUsnRead() ) )
  1880. continue;
  1881. // If we're in danger of a log overflow, process records.
  1882. if ( ulPct < (fSomethingChanged ? USN_LOG_DANGER_THRESHOLD + 5 : USN_LOG_DANGER_THRESHOLD ) )
  1883. {
  1884. ciDebugOut(( DEB_ITRACE, "Drive %wc: in danger (%u%%)\n",
  1885. pUsnVolume->DriveLetter(), pUsnVolume->PercentRead() ));
  1886. cInDanger++;
  1887. if ( !fSomethingChanged )
  1888. {
  1889. //
  1890. // Boost the priority to try and catch up before we fall farther behind.
  1891. // Normally, this thread runs at below normal priority.
  1892. //
  1893. priOriginal = GetThreadPriority( GetCurrentThread() );
  1894. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
  1895. fSomethingChanged = TRUE;
  1896. }
  1897. // TRUE == immediate processing
  1898. // FALSE == don't wait for notifications
  1899. NTSTATUS Status = ProcessUsnNotificationsFromVolume( pUsnVolume, TRUE, FALSE );
  1900. Win4Assert( STATUS_PENDING != Status );
  1901. //
  1902. // Even if this is STATUS_JOURNAL_ENTRY_DELETED, we might as
  1903. // well abort the scan. Another will have been scheduled.
  1904. //
  1905. if ( !NT_SUCCESS(Status) )
  1906. {
  1907. ciDebugOut(( DEB_ERROR, "Error %#x from ProcessUsnNotificationsFromVolume during scan\n", Status ));
  1908. THROW( CException( Status ) );
  1909. }
  1910. }
  1911. fFirst = FALSE;
  1912. ciDebugOut(( DEB_ITRACE, "Drive %wc: %u%% read (scan)\n",
  1913. pUsnVolume->DriveLetter(), ulPct ));
  1914. }
  1915. } while ( !fAbort && cInDanger > 0 );
  1916. //
  1917. // Update the max USNs
  1918. //
  1919. if ( fSomethingChanged )
  1920. {
  1921. //
  1922. // Reset thread priority.
  1923. //
  1924. if ( THREAD_PRIORITY_ERROR_RETURN != priOriginal )
  1925. SetThreadPriority( GetCurrentThread(), priOriginal );
  1926. for ( CUsnVolumeIter usnVolIter(usnVolumesToProcess);
  1927. !usnVolumesToProcess.AtEnd(usnVolIter);
  1928. usnVolumesToProcess.Advance(usnVolIter) )
  1929. {
  1930. //
  1931. // Patch the USN for the scan
  1932. //
  1933. if ( volScan == usnVolIter->VolumeId() )
  1934. usnScan = usnVolIter->MaxUsnRead();
  1935. for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
  1936. !_usnScansInProgress.AtEnd(scanInfoIter);
  1937. _usnScansInProgress.Advance(scanInfoIter) )
  1938. {
  1939. CCiScanInfo & scanInfo = * scanInfoIter.GetEntry();
  1940. if ( scanInfo.VolumeId() == usnVolIter->VolumeId() )
  1941. {
  1942. ciDebugOut(( DEB_ITRACE,
  1943. "usn updating scan usn on %wc: from %#I64x to %#I64x\n",
  1944. (WCHAR)scanInfo.VolumeId(),
  1945. scanInfo.UsnStart(),
  1946. usnVolIter->MaxUsnRead() ));
  1947. scanInfo.SetStartUsn( usnVolIter->MaxUsnRead() );
  1948. }
  1949. }
  1950. // Patch the USN for the monitored volume
  1951. for ( CUsnVolumeIter usnVolIter2(_usnVolumesToMonitor);
  1952. !_usnVolumesToMonitor.AtEnd(usnVolIter2);
  1953. _usnVolumesToMonitor.Advance(usnVolIter2) )
  1954. {
  1955. if ( usnVolIter->VolumeId() == usnVolIter2->VolumeId() )
  1956. {
  1957. ciDebugOut(( DEB_ITRACE,
  1958. "usn updating monitor volume %wc: from %#I64x to %#I64x\n",
  1959. usnVolIter->DriveLetter(),
  1960. usnVolIter->MaxUsnRead(),
  1961. usnVolIter->MaxUsnRead() ));
  1962. usnVolIter2->SetMaxUsnRead( usnVolIter->MaxUsnRead() );
  1963. }
  1964. }
  1965. }
  1966. }
  1967. } //ProcessUsnLog
  1968. //+---------------------------------------------------------------------------
  1969. //
  1970. // Member: CUsnMgr::HandleError, public
  1971. //
  1972. // Synopsis: Error reporting / handling for USN log read errors.
  1973. //
  1974. // Arguments: [pUsnVolume] -- Volume which could not be read.
  1975. // [Status] -- Failure code.
  1976. //
  1977. // History: 04-Jun-1998 KyleP Created
  1978. //
  1979. //----------------------------------------------------------------------------
  1980. void CUsnMgr::HandleError( CUsnVolume * pUsnVolume, NTSTATUS Status )
  1981. {
  1982. HandleError( pUsnVolume->DriveLetter(), Status );
  1983. if ( STATUS_TOO_LATE != Status )
  1984. pUsnVolume->MarkOffline();
  1985. }
  1986. //+---------------------------------------------------------------------------
  1987. //
  1988. // Member: CUsnMgr::HandleError, public
  1989. //
  1990. // Synopsis: Error reporting / handling for USN log read errors.
  1991. //
  1992. // Arguments: [wcDrive] -- Volume which could not be read.
  1993. // [Status] -- Failure code.
  1994. //
  1995. // History: 04-Jun-1998 KyleP Created
  1996. //
  1997. //----------------------------------------------------------------------------
  1998. void CUsnMgr::HandleError( WCHAR wcDrive, NTSTATUS Status )
  1999. {
  2000. Win4Assert( STATUS_SUCCESS != Status );
  2001. if ( STATUS_TOO_LATE != Status )
  2002. {
  2003. CEventLog eventLog( NULL, wcsCiEventSource );
  2004. CEventItem item( EVENTLOG_WARNING_TYPE,
  2005. CI_SERVICE_CATEGORY,
  2006. MSG_CI_USN_LOG_UNREADABLE,
  2007. 2 );
  2008. WCHAR wszDrive[] = L"A:";
  2009. wszDrive[0] = wcDrive;
  2010. item.AddArg( wszDrive );
  2011. item.AddError( Status );
  2012. eventLog.ReportEvent( item );
  2013. }
  2014. }
  2015. //+---------------------------------------------------------------------------
  2016. //
  2017. // Member: CUsnMgr::IsPathIndexed, public
  2018. //
  2019. // Synopsis: Returns TRUE if the path is being indexed on a USN volume.
  2020. //
  2021. // Arguments: [pUsnVolume] -- The volume to use when checking
  2022. // [lcaseFunnyPath] -- The path to check
  2023. //
  2024. // Returns: TRUE if the path is in a current scan or in pUsnVolume.
  2025. //
  2026. // History: 12-Jun-98 dlee Created
  2027. //
  2028. //----------------------------------------------------------------------------
  2029. BOOL CUsnMgr::IsPathIndexed(
  2030. CUsnVolume * pUsnVolume,
  2031. CLowerFunnyPath & lcaseFunnyPath )
  2032. {
  2033. //
  2034. // First check the volume object given
  2035. //
  2036. CScanInfoList & usnScopeList = pUsnVolume->GetUsnScopesList();
  2037. for ( CFwdScanInfoIter iter( usnScopeList );
  2038. !usnScopeList.AtEnd(iter);
  2039. usnScopeList.Advance(iter) )
  2040. {
  2041. CScopeMatch scopeMatch( iter->GetPath(),
  2042. wcslen( iter->GetPath() ) );
  2043. if ( scopeMatch.IsInScope( lcaseFunnyPath.GetActualPath(), lcaseFunnyPath.GetActualLength() ) )
  2044. return TRUE;
  2045. }
  2046. //
  2047. // Check the scans in progress list
  2048. //
  2049. for ( CFwdScanInfoIter scanInfoIter(_usnScansInProgress);
  2050. !_usnScansInProgress.AtEnd(scanInfoIter);
  2051. _usnScansInProgress.Advance(scanInfoIter) )
  2052. {
  2053. CCiScanInfo & scanInfo = * scanInfoIter.GetEntry();
  2054. WCHAR const * pwcScanPath = scanInfo.GetPath();
  2055. CScopeMatch scopeMatch( pwcScanPath, wcslen( pwcScanPath ) );
  2056. if ( scopeMatch.IsInScope( lcaseFunnyPath.GetActualPath(), lcaseFunnyPath.GetActualLength() ) )
  2057. return TRUE;
  2058. }
  2059. return FALSE;
  2060. } //IsPathIndexed
  2061. //+---------------------------------------------------------------------------
  2062. //
  2063. // Member: CUsnMgr::IsLowResource, private
  2064. //
  2065. // Synopsis: Determine if either memory or i/o resources are low.
  2066. //
  2067. // Returns: TRUE if in a low resource condition.
  2068. //
  2069. // History: 22-Jun-1998 KyleP Created
  2070. //
  2071. //----------------------------------------------------------------------------
  2072. BOOL CUsnMgr::IsLowResource()
  2073. {
  2074. if ( _cicat.GetRegParams()->DelayUsnReadOnLowResource() )
  2075. {
  2076. CI_STATE State;
  2077. State.cbStruct = sizeof( State );
  2078. SCODE sc = _cicat.CiState( State );
  2079. if ( SUCCEEDED( sc ) )
  2080. return ( 0 != (State.eState & ( CI_STATE_HIGH_IO |
  2081. CI_STATE_LOW_MEMORY |
  2082. CI_STATE_USER_ACTIVE ) ) );
  2083. }
  2084. return FALSE;
  2085. }
  2086. //+---------------------------------------------------------------------------
  2087. //
  2088. // Member: CUsnMgr::AnyInitialScans
  2089. //
  2090. // Synopsis: Checks if any scans are the result of a new scope
  2091. //
  2092. // Returns: TRUE if any scans are for new scopes
  2093. //
  2094. // History: 3-Aug-98 dlee Created
  2095. //
  2096. //----------------------------------------------------------------------------
  2097. BOOL CUsnMgr::AnyInitialScans()
  2098. {
  2099. for ( CFwdScanInfoIter iter1( _usnScansToDo );
  2100. !_usnScansToDo.AtEnd( iter1 );
  2101. _usnScansToDo.Advance( iter1 ) )
  2102. {
  2103. if ( iter1->IsNewScope() )
  2104. return TRUE;
  2105. }
  2106. for ( CFwdScanInfoIter iter2( _usnScansInProgress );
  2107. !_usnScansInProgress.AtEnd( iter2 );
  2108. _usnScansInProgress.Advance( iter2 ) )
  2109. {
  2110. if ( iter2->IsNewScope() )
  2111. return TRUE;
  2112. }
  2113. return FALSE;
  2114. } //AnyInitialScans
  2115. //+---------------------------------------------------------------------------
  2116. //
  2117. // Member: CUsnMgr::CheckTopLevelChange, private
  2118. //
  2119. // Synopsis: Checks if renames or creates *above* top level scopes need
  2120. // processing (because they affect top level).
  2121. //
  2122. // Arguments: [pUsnVolume] -- USN volume change affects
  2123. // [FileReferenceNumber] -- File ID of changing file
  2124. //
  2125. // History: 08-Jan-1999 KyleP Created
  2126. //
  2127. //----------------------------------------------------------------------------
  2128. void CUsnMgr::CheckTopLevelChange( CUsnVolume * pUsnVolume,
  2129. ULONGLONG & FileReferenceNumber )
  2130. {
  2131. //
  2132. // Convert file ID to path.
  2133. //
  2134. CLowerFunnyPath fpRenamed;
  2135. if ( 0 != _cicat.FileIdToPath( FileReferenceNumber, pUsnVolume->VolumeId(), fpRenamed ) )
  2136. {
  2137. CScopeMatch SMatch( fpRenamed.GetActualPath(), fpRenamed.GetActualLength() );
  2138. //
  2139. // Iterate through the top level scopes for this volume.
  2140. //
  2141. CScanInfoList & Scopes = pUsnVolume->GetUsnScopesList();
  2142. for ( CFwdScanInfoIter ScopesIter( Scopes );
  2143. !Scopes.AtEnd( ScopesIter );
  2144. Scopes.Advance( ScopesIter ) )
  2145. {
  2146. if ( _fAbort || _fUpdatesDisabled )
  2147. return;
  2148. WCHAR const * pwcsPath = ScopesIter->GetPath();
  2149. unsigned ccPath = wcslen(pwcsPath);
  2150. //
  2151. // Any top level scope that is underneath the rename must not have been
  2152. // read before, so we need to add all the files.
  2153. //
  2154. if ( SMatch.IsInScope( pwcsPath, ccPath ) )
  2155. {
  2156. ICiManager *pCiManager = _cicat.CiManager();
  2157. CLowerFunnyPath fpNew( pwcsPath, ccPath, TRUE );
  2158. fpNew.AppendBackSlash();
  2159. ciDebugOut(( DEB_USN, "Adding directory %ws\n", fpNew.GetActualPath() ));
  2160. _fDoingRenameTraverse = TRUE;
  2161. CUsnTreeTraversal usnTreeTrav( _cicat,
  2162. *this,
  2163. *pCiManager,
  2164. fpNew,
  2165. FALSE, // No deletions
  2166. _fUpdatesDisabled,
  2167. TRUE, // Process root
  2168. pUsnVolume->VolumeId() );
  2169. usnTreeTrav.EndProcessing();
  2170. _fDoingRenameTraverse = FALSE;
  2171. if ( _fAbort || _fUpdatesDisabled )
  2172. return;
  2173. SCODE sc = pCiManager->FlushUpdates();
  2174. if ( FAILED(sc) )
  2175. THROW( CException( sc ) );
  2176. }
  2177. //
  2178. // Conversely, if the top level scope no longer exists, files must be
  2179. // removed from the catalog. The LokIsInFinalState check is to keep
  2180. // us from retrying the same delete > once.
  2181. //
  2182. WIN32_FIND_DATA ffData;
  2183. if ( !GetFileAttributesEx( pwcsPath, GetFileExInfoStandard, &ffData ) &&
  2184. ScopesIter->LokIsInFinalState() )
  2185. {
  2186. ScopesIter->SetStartState();
  2187. CLowerFunnyPath fpOld( pwcsPath, ccPath, TRUE );
  2188. fpOld.AppendBackSlash();
  2189. ICiManager *pCiManager = _cicat.CiManager();
  2190. ciDebugOut(( DEB_USN, "Removing directory %ws\n", fpOld.GetActualPath() ));
  2191. _cicat.RemovePathsFromCiCat( fpOld.GetActualPath(), eUsnsArray );
  2192. if ( _fAbort || _fUpdatesDisabled )
  2193. return;
  2194. SCODE sc = pCiManager->FlushUpdates();
  2195. if ( FAILED(sc) )
  2196. THROW( CException( sc ) );
  2197. }
  2198. }
  2199. }
  2200. } //CheckTopLevelChange