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.

1159 lines
36 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 1999.
  5. //
  6. // File: FILTMAN.CXX
  7. //
  8. // Contents: Filter Manager
  9. //
  10. // Classes: CFilterManager
  11. //
  12. // History: 03-Jan-95 BartoszM Created from parts of CResMan
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <ciole.hxx>
  18. #include <cievtmsg.h>
  19. #include <fdaemon.hxx>
  20. #include <cifailte.hxx>
  21. #include "filtman.hxx"
  22. #include "resman.hxx"
  23. #include "fltstate.hxx"
  24. //+---------------------------------------------------------------------------
  25. //
  26. // Member: CFilterAgent::CFilterAgent, public
  27. //
  28. // Synopsis: Initialize the agent, register it with ResMan
  29. //
  30. // History: 05-Jan-95 BartoszM Created
  31. //
  32. //----------------------------------------------------------------------------
  33. CFilterAgent::CFilterAgent (CFilterManager& filterMan, CResManager& resman )
  34. : _sigFilterAgent(eSigFilterAgent),
  35. _resman (resman),
  36. _filterMan (filterMan),
  37. _eventUpdate(FALSE), // start reset
  38. _fStopFilter(FALSE)
  39. {
  40. _resman.RegisterFilterAgent (this);
  41. }
  42. //+---------------------------------------------------------------------------
  43. //
  44. // Member: CFilterAgent::~CFilterAgent, public
  45. //
  46. // Synopsis: Stop filtering
  47. //
  48. // History: 05-Jan-95 BartoszM Created
  49. //
  50. //----------------------------------------------------------------------------
  51. CFilterAgent::~CFilterAgent ()
  52. {
  53. if ( !_fStopFilter )
  54. {
  55. StopFiltering();
  56. }
  57. }
  58. void CFilterAgent::StopFiltering()
  59. {
  60. CLock lock(_mutex);
  61. _fStopFilter = TRUE;
  62. _eventUpdate.Set();
  63. }
  64. //+---------------------------------------------------------------------------
  65. //
  66. // Member: CFilterAgent::FilterThread, public
  67. //
  68. // Synopsis: Entry point for the thread responsible
  69. // for filtering
  70. //
  71. // History: 05-Jan-95 BartoszM Created
  72. //
  73. //----------------------------------------------------------------------------
  74. DWORD WINAPI CFilterAgent::FilterThread( void* p )
  75. {
  76. return S_OK;
  77. }
  78. //+---------------------------------------------------------------------------
  79. //
  80. // Function: PutToSleep
  81. //
  82. // Synopsis: Resets the update event unless filtering is stopped
  83. //
  84. // History: Feb-08-95 BartoszM Created
  85. //
  86. //----------------------------------------------------------------------------
  87. BOOL CFilterAgent::PutToSleep ()
  88. {
  89. CLock lock(_mutex);
  90. if (_fStopFilter)
  91. return FALSE;
  92. _eventUpdate.Reset();
  93. return TRUE;
  94. }
  95. //+---------------------------------------------------------------------------
  96. //
  97. // Function: LokWakeUp
  98. //
  99. // Synopsis: Completes an asynchronous pending I/O request, if any.
  100. //
  101. // History: 19-Nov-94 DwightKr Moved into a method
  102. //
  103. // Notes: If a corrupt content scan is detected, this routine
  104. // will persistently record the event to disk. This may not
  105. // throw.
  106. //
  107. //----------------------------------------------------------------------------
  108. void CFilterAgent::LokWakeUp ()
  109. {
  110. _eventUpdate.Set();
  111. }
  112. //+---------------------------------------------------------------------------
  113. //
  114. // Member: CFilterAgent::LokCancel, public
  115. //
  116. // Synopsis: Cancel any connections to the CI Filter service.
  117. //
  118. // History: 09-Jan-95 BartoszM Created
  119. //
  120. //----------------------------------------------------------------------------
  121. void CFilterAgent::LokCancel ()
  122. {
  123. _eventUpdate.Set(); // wake up
  124. }
  125. //+---------------------------------------------------------------------------
  126. //
  127. // Member: CFilterManager::CFilterManager, public
  128. //
  129. // Arguments: [resman] -- resource manager
  130. //
  131. // Requires: resman to be fully constructed
  132. //
  133. // History: 03-Jan-95 BartoszM Created
  134. //
  135. //----------------------------------------------------------------------------
  136. CFilterManager::CFilterManager(CResManager& resman)
  137. : _sigFiltMan(eSigFiltMan),
  138. _resman (resman),
  139. _docList(resman.GetDocList()),
  140. #pragma warning( disable : 4355 ) // this used in base initialization
  141. _filterAgent (*this, resman ),
  142. #pragma warning( default : 4355 )
  143. _fPushFiltering( resman.FPushFiltering() ),
  144. _LastResourceStatus( TRUE )
  145. {
  146. LARGE_INTEGER CurrentTime;
  147. NtQuerySystemTime( &CurrentTime );
  148. _LastResourceCheck = CurrentTime.QuadPart;
  149. _fWaitOnNoDocs = FALSE;
  150. }
  151. NTSTATUS CFilterManager::Dismount()
  152. {
  153. _filterAgent.StopFiltering();
  154. return STATUS_SUCCESS;
  155. }
  156. //+---------------------------------------------------------------------------
  157. //
  158. // Function: CleanupPartialWordList
  159. //
  160. // Synopsis: If there is any previous wordlist, it will cleanup the
  161. // wordlist by considering all the documents as failed with
  162. // FILTER_EXCEPTION. If the retry count has exceeded the
  163. // maximum for a document, it will be considered as UNFILTERED
  164. // and marked accordingly in the wordlist.
  165. //
  166. // This handling is necessary to deal with the case of a
  167. // repeatedly failing filter daemon on a document and to limit
  168. // the number of retries. Just refiling the documents will
  169. // lead to infinite looping.
  170. //
  171. // History: 6-16-94 srikants Separated from FilterReady for making
  172. // kernel mode call Asynchronous.
  173. //
  174. //----------------------------------------------------------------------------
  175. void CFilterManager::CleanupPartialWordList()
  176. {
  177. if ( !_pWordList.IsNull() )
  178. {
  179. //
  180. // We have to consider the filtering failed due to a problem in
  181. // the filter daemon and mark ALL these documents as FAILED.
  182. //
  183. ciDebugOut(( DEB_WARN,
  184. "CFilterManager::CleanupPartialWordList. "
  185. "Partially complete wordlist committed.\n" ));
  186. Win4Assert( 0 != _docList.Count() );
  187. STATUS aStatus[CI_MAX_DOCS_IN_WORDLIST];
  188. for ( unsigned i = 0; i < _docList.Count(); i++ )
  189. {
  190. aStatus[i] = FILTER_EXCEPTION;
  191. }
  192. FilterDone( aStatus, _docList.Count() );
  193. Win4Assert( _pWordList.IsNull() );
  194. Win4Assert( 0 == _docList.Count() );
  195. }
  196. else
  197. {
  198. Win4Assert( 0 == _docList.Count() );
  199. }
  200. }
  201. //+---------------------------------------------------------------------------
  202. //
  203. // Function: FilterReady
  204. //
  205. // Synopsis: User mode (down level) filter ready handler. The main
  206. // difference from the kernel mode one is that the caller
  207. // is made to WAIT for an event in this call where as in the
  208. // kernel mode call, it will be returned STATUS_PENDING if there
  209. // are no documents.
  210. //
  211. // Arguments: [docBuffer] -- <see above>
  212. // [cb] --
  213. // [cMaxDocs] --
  214. //
  215. // History: 6-16-94 srikants For user-mode synchronous call.
  216. //
  217. //----------------------------------------------------------------------------
  218. SCODE CFilterManager::FilterReady ( BYTE * docBuffer,
  219. ULONG & cb,
  220. ULONG cMaxDocs )
  221. {
  222. CleanupPartialWordList();
  223. SCODE scode = S_OK;
  224. ULONG cbMax = cb;
  225. TRY
  226. {
  227. ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterReady.\n" ));
  228. Win4Assert( _docList.Count() == 0 );
  229. ULONG cDocs = 0;
  230. CGetFilterDocsState stateInfo;
  231. // loop until there are enough resources to filter and there are documents
  232. // to filter.
  233. do
  234. {
  235. //
  236. // Have at least reasonable confidence that there is
  237. // enough free memory to build the wordlist. The minimum
  238. // free space and sleep time are parameterized by size
  239. // and sleep respectively.
  240. //
  241. BOOL fRetry = FALSE;
  242. Win4Assert( _pWordList.IsNull() );
  243. Win4Assert( _docList.Count() == 0 );
  244. cb = 0;
  245. BOOL fGoodTimeToFilter = FALSE;
  246. _resman.SampleUserActivity();
  247. // =================================================
  248. {
  249. CPriLock lock(_resman.GetMutex());
  250. fGoodTimeToFilter = LokIsGoodTimeToFilter();
  251. }
  252. // ==================================================
  253. if ( fGoodTimeToFilter )
  254. fGoodTimeToFilter = IsGoodTimeToFilter(); // Don't call this under lock.
  255. if ( !fGoodTimeToFilter )
  256. {
  257. ciDebugOut(( DEB_ITRACE, "Not a good time to filter.\n" ));
  258. }
  259. //
  260. // If the disk is getting full, don't give any more documents to
  261. // be filtered.
  262. //
  263. if ( fGoodTimeToFilter )
  264. fRetry = _resman.GetFilterDocs ( cMaxDocs, cDocs, stateInfo );
  265. if ( 0 == cDocs )
  266. {
  267. if (!_filterAgent.PutToSleep())
  268. return CI_E_SHUTDOWN;
  269. if ( fRetry )
  270. continue;
  271. // otherwise wait
  272. ciDebugOut (( DEB_ITRACE, "\t|Waiting For Event\n" ));
  273. _filterAgent.Wait( fGoodTimeToFilter ?
  274. INFINITE :
  275. _resman.GetRegParams().GetLowResourceSleep() * 1000 );
  276. stateInfo.Reset();
  277. ciDebugOut (( DEB_ITRACE, "\t|Wakeup!\n" ));
  278. _fWaitOnNoDocs = FALSE;
  279. }
  280. else
  281. {
  282. cb = cbMax;
  283. _resman.FillDocBuffer( docBuffer, cb, cDocs, stateInfo );
  284. if ( cb > cbMax )
  285. {
  286. // we need more space
  287. Win4Assert ( 0 == cDocs );
  288. break;
  289. }
  290. }
  291. } while ( cDocs == 0 );
  292. // There are documents to filter
  293. ciFAILTEST(STATUS_NO_MEMORY);
  294. if ( cb <= cbMax )
  295. {
  296. Win4Assert( _docList.Count() != 0 );
  297. NewWordList();
  298. }
  299. }
  300. CATCH( CException, e )
  301. {
  302. scode = e.GetErrorCode();
  303. if ( 0 != _docList.Count() )
  304. {
  305. //
  306. // We need to add these documents back to the change log
  307. // so that they will get filtered later.
  308. //
  309. Win4Assert( _pWordList.IsNull() );
  310. //
  311. // Lock will be obtained by NoFailReFile before mucking with
  312. // the doclist.
  313. //
  314. if (!_resman.NoFailReFile ( _resman.GetDocList() ))
  315. scode = STATUS_CANCELLED;
  316. }
  317. }
  318. END_CATCH
  319. _fWaitOnNoDocs = FALSE;
  320. return scode;
  321. }
  322. //+---------------------------------------------------------------------------
  323. //
  324. // Member: CFilterManager::LokIsGoodTimeToFilter
  325. //
  326. // Synopsis: Checks to see if this is a good time to filter.
  327. //
  328. // Returns: TRUE if okay to filter now.
  329. // FALSE o/w
  330. //
  331. // History: 4-12-96 srikants Made a version for user space.
  332. //
  333. //----------------------------------------------------------------------------
  334. BOOL CFilterManager::LokIsGoodTimeToFilter()
  335. {
  336. //
  337. // If a content scan is required, setup the returned
  338. // status.
  339. //
  340. if ( _resman.IsEmpty() )
  341. {
  342. //
  343. // The content index is being emptied. Don't give any
  344. // more documents to daemon.
  345. //
  346. return FALSE;
  347. }
  348. else if ( _resman.IsLowOnDiskSpace() )
  349. {
  350. //
  351. // Check if the disk status has improved and ask the
  352. // merge thread to do any book-keeping work.
  353. //
  354. if ( !_resman.LokCheckLowOnDiskSpace() )
  355. _resman.LokWakeupMergeThread();
  356. return FALSE;
  357. }
  358. else if ( _resman.LokCheckWordlistQuotas() )
  359. {
  360. //
  361. // We're already at our limit, so it's not a good idea to create more.
  362. //
  363. _resman.LokWakeupMergeThread();
  364. return FALSE;
  365. }
  366. else if ( _resman.LokIsScanNeeded() )
  367. {
  368. //
  369. // A scan is needed if we either lost an update or we are corrupt.
  370. //
  371. _resman.LokWakeupMergeThread();
  372. return FALSE;
  373. }
  374. return TRUE;
  375. }
  376. //+---------------------------------------------------------------------------
  377. //
  378. // Member: CFilterManager::IsGoodTimeToFilter
  379. //
  380. // Synopsis: Checks to see if this is a good time to filter.
  381. //
  382. // Returns: TRUE if okay to filter now.
  383. // FALSE o/w
  384. //
  385. // History: 12-Apr-96 srikants Made a version for user space.
  386. // 10-Dec-97 KyleP Make NoLok version
  387. //
  388. //----------------------------------------------------------------------------
  389. BOOL CFilterManager::IsGoodTimeToFilter()
  390. {
  391. //
  392. // Don't do any work until the system has booted
  393. //
  394. if ( GetTickCount() < _resman.GetRegParams().GetStartupDelay() )
  395. return FALSE;
  396. LARGE_INTEGER CurrentTime;
  397. NTSTATUS Status = NtQuerySystemTime( &CurrentTime );
  398. if ( NT_SUCCESS(Status) )
  399. {
  400. // Check user activity at 5 s. intervals
  401. if ( CurrentTime.QuadPart - _LastResourceCheck > 5 * 10000000i64 )
  402. {
  403. if ( _resman.IsUserActive(FALSE) )
  404. {
  405. //
  406. // If someone is typing on the keyboard, don't get in the way.
  407. //
  408. _LastResourceStatus = FALSE;
  409. _resman.ReportFilteringState( CIF_STATE_USER_ACTIVE );
  410. return _LastResourceStatus;
  411. }
  412. }
  413. ULONG ulDelay = _LastResourceStatus ?
  414. _resman.GetRegParams().GetWordlistResourceCheckInterval() :
  415. _resman.GetRegParams().GetLowResourceSleep();
  416. if ( CurrentTime.QuadPart - _LastResourceCheck > ulDelay * 10000000i64 )
  417. {
  418. _LastResourceCheck = CurrentTime.QuadPart;
  419. DWORD dwFilterState;
  420. if ( _resman.IsMemoryLow() )
  421. {
  422. //
  423. // If we're low on memory, don't eat up more with word lists
  424. //
  425. _LastResourceStatus = FALSE;
  426. dwFilterState = CIF_STATE_LOW_MEMORY;
  427. }
  428. else if ( _resman.IsBatteryLow() )
  429. {
  430. _LastResourceStatus = FALSE;
  431. dwFilterState = CIF_STATE_BATTERY_POWER;
  432. }
  433. else if ( _resman.IsIoHigh() )
  434. {
  435. //
  436. // If someone else is doing a lot of I/O, then we shouldn't add
  437. // to the problem.
  438. //
  439. _LastResourceStatus = FALSE;
  440. dwFilterState = CIF_STATE_HIGH_IO;
  441. }
  442. else if ( _resman.IsUserActive(TRUE) )
  443. {
  444. //
  445. // If someone is typing on the keyboard, don't get in the way.
  446. // This check is done after the Hi I/O check because the user
  447. // activity is sampled over 5 seconds there.
  448. //
  449. _LastResourceStatus = FALSE;
  450. dwFilterState = CIF_STATE_USER_ACTIVE;
  451. }
  452. else
  453. {
  454. _LastResourceStatus = TRUE;
  455. dwFilterState = 0;
  456. }
  457. _resman.ReportFilteringState( dwFilterState );
  458. }
  459. }
  460. return _LastResourceStatus;
  461. }
  462. #pragma warning( disable : 4756 ) // overflow in constant arithmetic
  463. //+---------------------------------------------------------------------------
  464. //
  465. // Member: CFilterManager::FilterDataReady, public
  466. //
  467. // Synopsis: Adds the contents of entryBuf to the current Word List.
  468. //
  469. // Returns: Whether the word list is full.
  470. //
  471. // Arguments: [pEntryBuf] -- pointer to data to be added to Word List
  472. // [cb] -- count of bytes in buffer
  473. //
  474. // History: 22-Mar-93 AmyA Created.
  475. //
  476. //----------------------------------------------------------------------------
  477. SCODE CFilterManager::FilterDataReady( const BYTE * pEntryBuf, ULONG cb )
  478. {
  479. if ( _pWordList.IsNull() )
  480. {
  481. ciDebugOut(( DEB_WARN,
  482. "CFilterManager::FilterDataReady. No wordlist active.\n" ));
  483. return FDAEMON_E_NOWORDLIST;
  484. }
  485. ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterDataReady.\n" ));
  486. SCODE sc = S_OK;
  487. TRY
  488. {
  489. _resman.SampleUserActivity();
  490. while (!_pWordList->MakeChunk( pEntryBuf, cb ))
  491. continue;
  492. if ( _pWordList->Size() >= _resman.GetRegParams().GetMaxWordlistSize() )
  493. {
  494. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDataReady -- Wordlist full\n" ));
  495. sc = FDAEMON_W_WORDLISTFULL;
  496. }
  497. if ( _resman.IsMemoryLow() )
  498. {
  499. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDataReady -- Running low on memory\n" ));
  500. sc = FDAEMON_W_WORDLISTFULL;
  501. //
  502. // Try forcing a merge to free things up.
  503. //
  504. _resman.ForceMerge( partidInvalid, CI_ANY_MERGE );
  505. }
  506. }
  507. CATCH( CException, e )
  508. {
  509. ciDebugOut(( DEB_ERROR,
  510. "Exception 0x%x caught during filtering. "
  511. "Rescheduling/aborting documents for filtering.\n",
  512. e.GetErrorCode() ));
  513. if ( e.GetErrorCode() == STATUS_INSUFFICIENT_RESOURCES )
  514. sc = FDAEMON_E_LOWRESOURCE;
  515. else
  516. sc = FDAEMON_E_FATALERROR;
  517. #if CIDBG == 1
  518. for ( unsigned cWid = 0; cWid < _docList.Count(); cWid++ )
  519. {
  520. ciDebugOut(( DEB_ITRACE,
  521. "Reschedule/aborting filtering of wid %d\n",
  522. _docList.Wid(cWid) ));
  523. }
  524. #endif // CIDBG == 1
  525. _pWordList.Delete();
  526. //
  527. // No refiling in push filtering, hence abort wids. Refile docList
  528. // in pull filtering.
  529. //
  530. if ( _fPushFiltering )
  531. _resman.NoFailAbortWidsInDocList();
  532. else
  533. _resman.NoFailReFile( _resman.GetDocList() );
  534. ciDebugOut(( DEB_ITRACE,
  535. "CFilterManager::FilterDataReady "
  536. "deleting wordlist and clearing doclist\n" ));
  537. }
  538. END_CATCH
  539. if ( sc == FDAEMON_E_LOWRESOURCE )
  540. _resman.NoFailFreeResources();
  541. #if CIDBG==1
  542. {
  543. CPriLock lock(_resman.GetMutex());
  544. Win4Assert( 0 == _docList.Count() && _pWordList.IsNull() ||
  545. 0 != _docList.Count() && !_pWordList.IsNull() );
  546. }
  547. #endif // CIDBG == 1
  548. return sc;
  549. }
  550. //+---------------------------------------------------------------------------
  551. //
  552. // Member: CFilterManager::FilterDone, public
  553. //
  554. // Synopsis: Commits the current wordlist.
  555. //
  556. // Arguments: [aStatus] -- array of STATUS for current document list
  557. // [count] -- count for array
  558. //
  559. // History: 22-Mar-93 AmyA Created from MakeWordList
  560. //
  561. //----------------------------------------------------------------------------
  562. SCODE CFilterManager::FilterDone( const STATUS * aStatus, ULONG count )
  563. {
  564. if ( _pWordList.IsNull() )
  565. {
  566. Win4Assert( 0 == _docList.Count() );
  567. ciDebugOut(( DEB_WARN,
  568. "CFilterManager::FilterDone. No wordlist active.\n" ));
  569. return( FDAEMON_E_NOWORDLIST );
  570. }
  571. ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterDone.\n" ));
  572. _resman.SampleUserActivity();
  573. // add statuses to _docList
  574. unsigned cDocuments = _docList.Count();
  575. Win4Assert( cDocuments <= count );
  576. for ( unsigned i = 0; i < cDocuments; i++ )
  577. {
  578. //
  579. // Note - we are modifying the status on the _docList in resman
  580. // without obtaining the resman lock. This is okay as we need a lock
  581. // only to protect the count in doclist.
  582. // Note: retries are 1-based, so the first filter attempt is 1.
  583. //
  584. if ( _docList.Retries(i) > _resman.GetRegParams().GetFilterRetries() )
  585. {
  586. _resman.GetDocList().SetStatus( i, TOO_MANY_RETRIES );
  587. }
  588. else if ( CI_SHARING_VIOLATION == aStatus[i] &&
  589. _docList.SecQRetries(i) >= _resman.GetRegParams().GetSecQFilterRetries() )
  590. {
  591. // Documents are put in sec Q only in case of sharing violation.
  592. // Hence the above check.
  593. ciDebugOut(( DEB_IWARN, "TOO_MANY_SECQ_RETRIES Doc Wid = %ld, SecQRetries = %d\n",
  594. _docList.Wid(i),
  595. _docList.SecQRetries(i) ));
  596. _resman.GetDocList().SetStatus( i, TOO_MANY_SECQ_RETRIES );
  597. }
  598. else if ( _docList.Status(i) != DELETED)
  599. {
  600. if ( _pWordList->IsEmpty() &&
  601. ( WL_IGNORE != aStatus[i] &&
  602. WL_NULL != aStatus[i] )
  603. && CI_SHARING_VIOLATION != aStatus[i] )
  604. {
  605. if ( CI_NOT_REACHABLE == aStatus[i] )
  606. {
  607. ciDebugOut (( DEB_WARN, "Wid %ld is UNREACHABLE\n",
  608. _docList.Wid(i) ));
  609. _resman.GetDocList().SetStatus( i, TOO_MANY_RETRIES );
  610. _resman.MarkUnReachable( _docList.Wid(i) );
  611. }
  612. else
  613. {
  614. //
  615. // If the wordlist is emtpy, it means the filter did not
  616. // give us any data but still called FilterDone. We should
  617. // treat all the documents as "exceptions".
  618. //
  619. ciDebugOut (( DEB_IWARN, "setting %d'th document"
  620. " with wid %ld, status %d to exception status\n",
  621. i, _docList.Wid(i), aStatus[i] ));
  622. //
  623. // Okay not to obtain the lock - only modifying the status.
  624. //
  625. _resman.GetDocList().SetStatus( i, FILTER_EXCEPTION );
  626. }
  627. }
  628. else
  629. {
  630. _resman.GetDocList().SetStatus( i, aStatus[i] );
  631. }
  632. }
  633. }
  634. PARTITIONID partid = _docList.PartId();
  635. INDEXID iid = _pWordList->GetId();
  636. SCODE sc = 0;
  637. BOOL fRetryFailures = FALSE;
  638. BOOL fDone = TRUE; // This is so FilterMore doesn't
  639. // get messed up. Eventually, FilterDone
  640. // should be split into FilterDone and
  641. // CommitWordList.
  642. {
  643. //=============================================
  644. CPriLock lock ( _resman.GetMutex() );
  645. // Resman is under lock till the end of block
  646. //=============================================
  647. if ( _resman.IsEmpty() ) // Content index empty
  648. {
  649. _resman.GetDocList().LokClear();
  650. return STATUS_CANCELLED;
  651. }
  652. // Special cases:
  653. // 1. partition has been deleted
  654. // - entries in changes and fresh have been removed
  655. // - delete wordlist and return
  656. // 2. wordlist is empty
  657. // - delete wordlist
  658. // - update changes
  659. // 3. no available index id's
  660. // - put docList back into changes
  661. // - delete word list
  662. // - force merge
  663. // 4. no success statuses
  664. // - delete word list
  665. iid = _resman.LokMakeWordlistId (partid);
  666. if (iid == iidInvalid)
  667. {
  668. ciDebugOut (( DEB_IWARN, "Invalid partition, Deleting wordlist\n" ));
  669. _pWordList.Delete();
  670. _resman.GetDocList().LokClear();
  671. return( FDAEMON_E_PARTITIONDELETED );
  672. }
  673. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
  674. "new wordlist id %lx\n", iid ));
  675. Win4Assert( iid != iidInvalid );
  676. _pWordList->SetId ( iid );
  677. BOOL successes = FALSE;
  678. for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
  679. {
  680. switch ( _docList.Status(iDoc) )
  681. {
  682. case TOO_MANY_RETRIES:
  683. case TOO_MANY_SECQ_RETRIES:
  684. if ( !fRetryFailures )
  685. {
  686. //
  687. // Save the state of the retry failures in the retry
  688. // fail list before they are overwritten.
  689. //
  690. _retryFailList = _docList;
  691. fRetryFailures = TRUE;
  692. }
  693. // NOTE: fall through
  694. case TOO_MANY_BLOCKS_TO_FILTER:
  695. successes = TRUE;
  696. _pWordList->MarkWidUnfiltered(iDoc);
  697. _resman.GetDocList().SetStatus( iDoc, SUCCESS );
  698. break;
  699. case SUCCESS:
  700. _resman.IncrementFilteredDocumentCount();
  701. successes = TRUE;
  702. break;
  703. case CI_SHARING_VIOLATION:
  704. //
  705. // If push filtering, abort the wid. If pull filtering,
  706. // add the wid to the sec queue so that filtering can be
  707. // re-attempted later.
  708. //
  709. if ( _fPushFiltering )
  710. _resman.NoFailAbortWid( _docList.Wid(iDoc), _docList.Usn(iDoc) );
  711. else
  712. {
  713. _resman.GetDocList().LokIncrementSecQRetryCount(iDoc);
  714. _resman.LokAddToSecQueue( partid,
  715. _docList.Wid(iDoc),
  716. _docList.VolumeId( iDoc ),
  717. _docList.SecQRetries( iDoc ) );
  718. }
  719. _resman.GetDocList().SetStatus( iDoc, WL_NULL );
  720. // WL_NULL is returned when the filter deliberately does
  721. // not return any data. Like DELETED, but we need to also
  722. // delete any existing data in the index.
  723. case WL_NULL:
  724. _pWordList->DeleteWidData(iDoc);
  725. // NOTE: fall through
  726. case DELETED:
  727. successes = TRUE;
  728. break;
  729. case WL_IGNORE:
  730. _pWordList->DeleteWidData(iDoc);
  731. if ( iDoc == cDocuments - 1 )
  732. {
  733. ciDebugOut (( DEB_ITRACE, ">> Not all filtered\n" ));
  734. fDone = FALSE;
  735. }
  736. break;
  737. case PENDING:
  738. Win4Assert( !"Ci Daemon Still returning Pending" );
  739. //
  740. // NOTE: Fall through is deliberate - should remove PENDING
  741. // when we find out we don't need it.
  742. //
  743. case PREEMPTED:
  744. //
  745. // NOTE: Fall through is deliberate.
  746. //
  747. default:
  748. ciDebugOut (( DEB_ITRACE,"++++ %d'th document"
  749. " with fake wid %d and wid %ld failed: status %d\n",
  750. iDoc, iDocToFakeWid(iDoc),
  751. _docList.Wid(iDoc), _docList.Status(iDoc) ));
  752. //
  753. // something went wrong during filtering. Delete all data
  754. // associated with this workid.
  755. //
  756. _resman.GetDocList().LokIncrementRetryCount(iDoc);
  757. _pWordList->DeleteWidData(iDoc);
  758. Win4Assert( _docList.Retries(iDoc) <= (_resman.GetRegParams().GetFilterRetries()+1) );
  759. //
  760. // If push filtering abort the wid. If pull filtering add it back
  761. // to the change log for re-filtering at a later time.
  762. //
  763. if ( _fPushFiltering )
  764. _resman.NoFailAbortWid( _docList.Wid(iDoc), _docList.Usn(iDoc) );
  765. else
  766. _resman.LokReFileDocument( partid,
  767. _docList.Wid(iDoc),
  768. _docList.Usn(iDoc),
  769. _docList.VolumeId(iDoc),
  770. _docList.Retries(iDoc),
  771. _docList.SecQRetries(iDoc) );
  772. break;
  773. }
  774. }
  775. if ( !successes ) // only invalid data in word list
  776. {
  777. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
  778. "deleting wordlist with invalid data\n" ));
  779. _pWordList.Delete();
  780. sc = FDAEMON_W_EMPTYWORDLIST;
  781. }
  782. else if ( !_resman.LokTransferWordlist ( _pWordList ) )
  783. {
  784. //
  785. // If push filtering, abort all wids. If pull filtering,
  786. // refile all wids.
  787. //
  788. if ( _fPushFiltering)
  789. _resman.NoFailAbortWidsInDocList();
  790. else
  791. _resman.NoFailReFile( _resman.GetDocList() );
  792. _pWordList.Delete();
  793. return( FDAEMON_E_WORDLISTCOMMITFAILED );
  794. }
  795. if ( fDone && (WL_IGNORE != aStatus[cDocuments-1]) )
  796. {
  797. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
  798. "clearing doclist\n" ));
  799. _resman.GetDocList().LokClear();
  800. }
  801. _resman.LokUpdateCounters(); // Update # filtred documents
  802. }
  803. //==========================================
  804. //
  805. // Notification of failed docs cannot be done under lock.
  806. //
  807. if ( fRetryFailures )
  808. {
  809. for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
  810. {
  811. if ( ( _retryFailList.Status(iDoc) == TOO_MANY_RETRIES ||
  812. _retryFailList.Status(iDoc) == TOO_MANY_SECQ_RETRIES ) &&
  813. CI_NOT_REACHABLE != aStatus[iDoc] )
  814. _resman.ReportFilterFailure (_retryFailList.Wid(iDoc));
  815. }
  816. }
  817. return sc;
  818. }
  819. //+---------------------------------------------------------------------------
  820. //
  821. // Member: CFilterManager::StoreValue
  822. //
  823. // Synopsis: Store a property value.
  824. //
  825. // Arguments: [widFake] -- Fake wid of object.
  826. // [prop] -- Property descriptor
  827. // [var] -- Value
  828. // [fCanStore] -- Returns TRUE if this is a storable property
  829. //
  830. // History: 21-Dec-95 KyleP Created
  831. //
  832. //----------------------------------------------------------------------------
  833. SCODE CFilterManager::FilterStoreValue( WORKID widFake,
  834. CFullPropSpec const & ps,
  835. CStorageVariant const & var,
  836. BOOL & fCanStore )
  837. {
  838. fCanStore = FALSE;
  839. Win4Assert( widFake > 0 );
  840. if ( widFake <= 0 )
  841. return STATUS_INVALID_PARAMETER;
  842. widFake = FakeWidToIndex( widFake );
  843. if ( widFake >= _docList.Count() )
  844. return STATUS_INVALID_PARAMETER;
  845. return _resman.StoreValue( _docList.Wid( widFake ), ps, var, fCanStore );
  846. }
  847. //+---------------------------------------------------------------------------
  848. //
  849. // Member: CFilterManager::StoreSecurity
  850. //
  851. // Synopsis: Store a file's security descriptor.
  852. //
  853. // Arguments: [widFake] -- Fake wid of object.
  854. // [pSD] -- pointer to security descriptor
  855. // [cbSD] -- size in bytes of pSD
  856. // [fCanStore] -- Returns TRUE if storae succeeded
  857. //
  858. // Notes: Used only for downlevel index
  859. //
  860. // History: 21-Dec-95 KyleP Created
  861. //
  862. //----------------------------------------------------------------------------
  863. SCODE CFilterManager::FilterStoreSecurity( WORKID widFake,
  864. PSECURITY_DESCRIPTOR pSD,
  865. ULONG cbSD,
  866. BOOL & fCanStore )
  867. {
  868. fCanStore = FALSE;
  869. Win4Assert( widFake > 0 );
  870. if ( widFake <= 0 )
  871. return STATUS_INVALID_PARAMETER;
  872. widFake = FakeWidToIndex( widFake );
  873. if ( widFake >= _docList.Count() )
  874. return STATUS_INVALID_PARAMETER;
  875. fCanStore = _resman.StoreSecurity( _docList.Wid( widFake ), pSD, cbSD );
  876. return S_OK;
  877. }
  878. //+---------------------------------------------------------------------------
  879. //
  880. // Member: CFilterManager::FPSToPROPID, public
  881. //
  882. // Synopsis: Converts FULLPROPSPEC property to PROPID
  883. //
  884. // Arguments: [fps] -- FULLPROPSPEC representation of property
  885. // [pid] -- PROPID written here on success
  886. //
  887. // Returns: S_OK on success
  888. //
  889. // History: 29-Dec-1997 KyleP Created.
  890. //
  891. //----------------------------------------------------------------------------
  892. SCODE CFilterManager::FPSToPROPID( CFullPropSpec const & fps, PROPID & pid )
  893. {
  894. return _resman.FPSToPROPID( fps, pid );
  895. }
  896. #pragma warning( default : 4756 ) // overflow in constant arithmetic
  897. //+---------------------------------------------------------------------------
  898. //
  899. // Member: CFilterManager::FilterMore, public
  900. //
  901. // Synopsis: Commits the current wordlist and creates a new one.
  902. //
  903. // Arguments: [aStatus] -- array of STATUS for the current document list
  904. // [count] -- count for array
  905. //
  906. // History: 03-May-93 AmyA Created.
  907. //
  908. //----------------------------------------------------------------------------
  909. SCODE CFilterManager::FilterMore( const STATUS * aStatus, ULONG count )
  910. {
  911. ciDebugOut (( DEB_ITRACE, "# CFilterManager::FilterMore\n" ));
  912. if ( _pWordList.IsNull() )
  913. {
  914. ciDebugOut(( DEB_WARN,
  915. "FilterMore: No wordlist active.\n" ));
  916. return( FDAEMON_E_NOWORDLIST );
  917. }
  918. SCODE sc = FilterDone( aStatus, count ); // Commits Word List
  919. if ( SUCCEEDED(sc) ) // No problems occured
  920. {
  921. // _docList may have been cleared for an error condition.
  922. ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterMore "
  923. "_docList.Count() = %d\n", _docList.Count() ));
  924. TRY
  925. {
  926. ciFAILTEST(STATUS_NO_MEMORY);
  927. if ( _docList.Count() != 0 )
  928. NewWordList(); // Creates New Word List
  929. }
  930. CATCH( CException, e )
  931. {
  932. sc = e.GetErrorCode();
  933. Win4Assert( _pWordList.IsNull() );
  934. //
  935. // If push filtering, abort wids. In pull filtering add these documents
  936. // back to the change log so that they will get filtered later.
  937. //
  938. if ( _fPushFiltering )
  939. _resman.NoFailAbortWidsInDocList();
  940. else
  941. {
  942. if (!_resman.NoFailReFile ( _resman.GetDocList() ))
  943. sc = STATUS_CANCELLED;
  944. }
  945. }
  946. END_CATCH
  947. }
  948. #if CIDBG==1
  949. {
  950. CPriLock lock(_resman.GetMutex());
  951. Win4Assert( 0 == _docList.Count() && _pWordList.IsNull() ||
  952. 0 != _docList.Count() && !_pWordList.IsNull() );
  953. }
  954. #endif // CIDBG == 1
  955. return sc;
  956. }
  957. //+---------------------------------------------------------------------------
  958. //
  959. // Member: CFilterManager::NewWordList, public
  960. //
  961. // Synopsis: Creates word list
  962. //
  963. // History: 08-Apr-91 BartoszM Created
  964. // 24-Nov-92 KyleP Retry pending documents
  965. // 22-Mar-93 AmyA Split into NewWordList and
  966. // CommitWordList
  967. //
  968. //----------------------------------------------------------------------------
  969. void CFilterManager::NewWordList()
  970. {
  971. unsigned cDocuments = _docList.Count();
  972. Win4Assert ( cDocuments != 0 );
  973. PARTITIONID partid = _docList.PartId();
  974. WORKID widMax = _docList.WidMax();
  975. CIndexId iid ( iidInvalid, partid );
  976. ciDebugOut (( DEB_ITRACE, "NewWordlist. Temporary iid %lx\n", iid ));
  977. _pWordList.Initialize ( iid, widMax );
  978. // initialize the wid translation table
  979. for ( unsigned i = 0; i < cDocuments; i++ )
  980. _pWordList->AddWid( _docList.Wid(i), _docList.VolumeId(i) );
  981. }