1160 lines
36 KiB
C++
1160 lines
36 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1991 - 1999.
|
||
|
//
|
||
|
// File: FILTMAN.CXX
|
||
|
//
|
||
|
// Contents: Filter Manager
|
||
|
//
|
||
|
// Classes: CFilterManager
|
||
|
//
|
||
|
// History: 03-Jan-95 BartoszM Created from parts of CResMan
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <pch.cxx>
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <ciole.hxx>
|
||
|
#include <cievtmsg.h>
|
||
|
#include <fdaemon.hxx>
|
||
|
#include <cifailte.hxx>
|
||
|
|
||
|
#include "filtman.hxx"
|
||
|
#include "resman.hxx"
|
||
|
#include "fltstate.hxx"
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterAgent::CFilterAgent, public
|
||
|
//
|
||
|
// Synopsis: Initialize the agent, register it with ResMan
|
||
|
//
|
||
|
// History: 05-Jan-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
CFilterAgent::CFilterAgent (CFilterManager& filterMan, CResManager& resman )
|
||
|
: _sigFilterAgent(eSigFilterAgent),
|
||
|
_resman (resman),
|
||
|
_filterMan (filterMan),
|
||
|
_eventUpdate(FALSE), // start reset
|
||
|
_fStopFilter(FALSE)
|
||
|
{
|
||
|
_resman.RegisterFilterAgent (this);
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterAgent::~CFilterAgent, public
|
||
|
//
|
||
|
// Synopsis: Stop filtering
|
||
|
//
|
||
|
// History: 05-Jan-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
CFilterAgent::~CFilterAgent ()
|
||
|
{
|
||
|
if ( !_fStopFilter )
|
||
|
{
|
||
|
StopFiltering();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CFilterAgent::StopFiltering()
|
||
|
{
|
||
|
CLock lock(_mutex);
|
||
|
_fStopFilter = TRUE;
|
||
|
_eventUpdate.Set();
|
||
|
}
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterAgent::FilterThread, public
|
||
|
//
|
||
|
// Synopsis: Entry point for the thread responsible
|
||
|
// for filtering
|
||
|
//
|
||
|
// History: 05-Jan-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
DWORD WINAPI CFilterAgent::FilterThread( void* p )
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PutToSleep
|
||
|
//
|
||
|
// Synopsis: Resets the update event unless filtering is stopped
|
||
|
//
|
||
|
// History: Feb-08-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
BOOL CFilterAgent::PutToSleep ()
|
||
|
{
|
||
|
CLock lock(_mutex);
|
||
|
|
||
|
if (_fStopFilter)
|
||
|
return FALSE;
|
||
|
|
||
|
_eventUpdate.Reset();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: LokWakeUp
|
||
|
//
|
||
|
// Synopsis: Completes an asynchronous pending I/O request, if any.
|
||
|
//
|
||
|
// History: 19-Nov-94 DwightKr Moved into a method
|
||
|
//
|
||
|
// Notes: If a corrupt content scan is detected, this routine
|
||
|
// will persistently record the event to disk. This may not
|
||
|
// throw.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
void CFilterAgent::LokWakeUp ()
|
||
|
{
|
||
|
_eventUpdate.Set();
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterAgent::LokCancel, public
|
||
|
//
|
||
|
// Synopsis: Cancel any connections to the CI Filter service.
|
||
|
//
|
||
|
// History: 09-Jan-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
void CFilterAgent::LokCancel ()
|
||
|
{
|
||
|
_eventUpdate.Set(); // wake up
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::CFilterManager, public
|
||
|
//
|
||
|
// Arguments: [resman] -- resource manager
|
||
|
//
|
||
|
// Requires: resman to be fully constructed
|
||
|
//
|
||
|
// History: 03-Jan-95 BartoszM Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
CFilterManager::CFilterManager(CResManager& resman)
|
||
|
: _sigFiltMan(eSigFiltMan),
|
||
|
_resman (resman),
|
||
|
_docList(resman.GetDocList()),
|
||
|
#pragma warning( disable : 4355 ) // this used in base initialization
|
||
|
_filterAgent (*this, resman ),
|
||
|
#pragma warning( default : 4355 )
|
||
|
_fPushFiltering( resman.FPushFiltering() ),
|
||
|
_LastResourceStatus( TRUE )
|
||
|
{
|
||
|
LARGE_INTEGER CurrentTime;
|
||
|
NtQuerySystemTime( &CurrentTime );
|
||
|
_LastResourceCheck = CurrentTime.QuadPart;
|
||
|
|
||
|
_fWaitOnNoDocs = FALSE;
|
||
|
}
|
||
|
|
||
|
NTSTATUS CFilterManager::Dismount()
|
||
|
{
|
||
|
_filterAgent.StopFiltering();
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: CleanupPartialWordList
|
||
|
//
|
||
|
// Synopsis: If there is any previous wordlist, it will cleanup the
|
||
|
// wordlist by considering all the documents as failed with
|
||
|
// FILTER_EXCEPTION. If the retry count has exceeded the
|
||
|
// maximum for a document, it will be considered as UNFILTERED
|
||
|
// and marked accordingly in the wordlist.
|
||
|
//
|
||
|
// This handling is necessary to deal with the case of a
|
||
|
// repeatedly failing filter daemon on a document and to limit
|
||
|
// the number of retries. Just refiling the documents will
|
||
|
// lead to infinite looping.
|
||
|
//
|
||
|
// History: 6-16-94 srikants Separated from FilterReady for making
|
||
|
// kernel mode call Asynchronous.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CFilterManager::CleanupPartialWordList()
|
||
|
{
|
||
|
if ( !_pWordList.IsNull() )
|
||
|
{
|
||
|
//
|
||
|
// We have to consider the filtering failed due to a problem in
|
||
|
// the filter daemon and mark ALL these documents as FAILED.
|
||
|
//
|
||
|
ciDebugOut(( DEB_WARN,
|
||
|
"CFilterManager::CleanupPartialWordList. "
|
||
|
"Partially complete wordlist committed.\n" ));
|
||
|
|
||
|
Win4Assert( 0 != _docList.Count() );
|
||
|
|
||
|
STATUS aStatus[CI_MAX_DOCS_IN_WORDLIST];
|
||
|
for ( unsigned i = 0; i < _docList.Count(); i++ )
|
||
|
{
|
||
|
aStatus[i] = FILTER_EXCEPTION;
|
||
|
}
|
||
|
|
||
|
FilterDone( aStatus, _docList.Count() );
|
||
|
|
||
|
Win4Assert( _pWordList.IsNull() );
|
||
|
Win4Assert( 0 == _docList.Count() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Win4Assert( 0 == _docList.Count() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: FilterReady
|
||
|
//
|
||
|
// Synopsis: User mode (down level) filter ready handler. The main
|
||
|
// difference from the kernel mode one is that the caller
|
||
|
// is made to WAIT for an event in this call where as in the
|
||
|
// kernel mode call, it will be returned STATUS_PENDING if there
|
||
|
// are no documents.
|
||
|
//
|
||
|
// Arguments: [docBuffer] -- <see above>
|
||
|
// [cb] --
|
||
|
// [cMaxDocs] --
|
||
|
//
|
||
|
// History: 6-16-94 srikants For user-mode synchronous call.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterReady ( BYTE * docBuffer,
|
||
|
ULONG & cb,
|
||
|
ULONG cMaxDocs )
|
||
|
{
|
||
|
CleanupPartialWordList();
|
||
|
SCODE scode = S_OK;
|
||
|
|
||
|
ULONG cbMax = cb;
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterReady.\n" ));
|
||
|
Win4Assert( _docList.Count() == 0 );
|
||
|
|
||
|
ULONG cDocs = 0;
|
||
|
CGetFilterDocsState stateInfo;
|
||
|
|
||
|
// loop until there are enough resources to filter and there are documents
|
||
|
// to filter.
|
||
|
do
|
||
|
{
|
||
|
//
|
||
|
// Have at least reasonable confidence that there is
|
||
|
// enough free memory to build the wordlist. The minimum
|
||
|
// free space and sleep time are parameterized by size
|
||
|
// and sleep respectively.
|
||
|
//
|
||
|
|
||
|
BOOL fRetry = FALSE;
|
||
|
|
||
|
Win4Assert( _pWordList.IsNull() );
|
||
|
Win4Assert( _docList.Count() == 0 );
|
||
|
|
||
|
cb = 0;
|
||
|
|
||
|
BOOL fGoodTimeToFilter = FALSE;
|
||
|
|
||
|
_resman.SampleUserActivity();
|
||
|
|
||
|
// =================================================
|
||
|
{
|
||
|
CPriLock lock(_resman.GetMutex());
|
||
|
fGoodTimeToFilter = LokIsGoodTimeToFilter();
|
||
|
}
|
||
|
// ==================================================
|
||
|
|
||
|
if ( fGoodTimeToFilter )
|
||
|
fGoodTimeToFilter = IsGoodTimeToFilter(); // Don't call this under lock.
|
||
|
|
||
|
if ( !fGoodTimeToFilter )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ITRACE, "Not a good time to filter.\n" ));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the disk is getting full, don't give any more documents to
|
||
|
// be filtered.
|
||
|
//
|
||
|
if ( fGoodTimeToFilter )
|
||
|
fRetry = _resman.GetFilterDocs ( cMaxDocs, cDocs, stateInfo );
|
||
|
|
||
|
|
||
|
|
||
|
if ( 0 == cDocs )
|
||
|
{
|
||
|
if (!_filterAgent.PutToSleep())
|
||
|
return CI_E_SHUTDOWN;
|
||
|
|
||
|
if ( fRetry )
|
||
|
continue;
|
||
|
|
||
|
// otherwise wait
|
||
|
ciDebugOut (( DEB_ITRACE, "\t|Waiting For Event\n" ));
|
||
|
_filterAgent.Wait( fGoodTimeToFilter ?
|
||
|
INFINITE :
|
||
|
_resman.GetRegParams().GetLowResourceSleep() * 1000 );
|
||
|
stateInfo.Reset();
|
||
|
ciDebugOut (( DEB_ITRACE, "\t|Wakeup!\n" ));
|
||
|
|
||
|
_fWaitOnNoDocs = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cb = cbMax;
|
||
|
_resman.FillDocBuffer( docBuffer, cb, cDocs, stateInfo );
|
||
|
if ( cb > cbMax )
|
||
|
{
|
||
|
// we need more space
|
||
|
Win4Assert ( 0 == cDocs );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while ( cDocs == 0 );
|
||
|
|
||
|
// There are documents to filter
|
||
|
|
||
|
ciFAILTEST(STATUS_NO_MEMORY);
|
||
|
|
||
|
if ( cb <= cbMax )
|
||
|
{
|
||
|
Win4Assert( _docList.Count() != 0 );
|
||
|
|
||
|
NewWordList();
|
||
|
}
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
scode = e.GetErrorCode();
|
||
|
|
||
|
if ( 0 != _docList.Count() )
|
||
|
{
|
||
|
//
|
||
|
// We need to add these documents back to the change log
|
||
|
// so that they will get filtered later.
|
||
|
//
|
||
|
Win4Assert( _pWordList.IsNull() );
|
||
|
|
||
|
//
|
||
|
// Lock will be obtained by NoFailReFile before mucking with
|
||
|
// the doclist.
|
||
|
//
|
||
|
if (!_resman.NoFailReFile ( _resman.GetDocList() ))
|
||
|
scode = STATUS_CANCELLED;
|
||
|
}
|
||
|
}
|
||
|
END_CATCH
|
||
|
|
||
|
_fWaitOnNoDocs = FALSE;
|
||
|
return scode;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::LokIsGoodTimeToFilter
|
||
|
//
|
||
|
// Synopsis: Checks to see if this is a good time to filter.
|
||
|
//
|
||
|
// Returns: TRUE if okay to filter now.
|
||
|
// FALSE o/w
|
||
|
//
|
||
|
// History: 4-12-96 srikants Made a version for user space.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CFilterManager::LokIsGoodTimeToFilter()
|
||
|
{
|
||
|
//
|
||
|
// If a content scan is required, setup the returned
|
||
|
// status.
|
||
|
//
|
||
|
if ( _resman.IsEmpty() )
|
||
|
{
|
||
|
//
|
||
|
// The content index is being emptied. Don't give any
|
||
|
// more documents to daemon.
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
else if ( _resman.IsLowOnDiskSpace() )
|
||
|
{
|
||
|
//
|
||
|
// Check if the disk status has improved and ask the
|
||
|
// merge thread to do any book-keeping work.
|
||
|
//
|
||
|
if ( !_resman.LokCheckLowOnDiskSpace() )
|
||
|
_resman.LokWakeupMergeThread();
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
else if ( _resman.LokCheckWordlistQuotas() )
|
||
|
{
|
||
|
//
|
||
|
// We're already at our limit, so it's not a good idea to create more.
|
||
|
//
|
||
|
|
||
|
_resman.LokWakeupMergeThread();
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
else if ( _resman.LokIsScanNeeded() )
|
||
|
{
|
||
|
//
|
||
|
// A scan is needed if we either lost an update or we are corrupt.
|
||
|
//
|
||
|
_resman.LokWakeupMergeThread();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::IsGoodTimeToFilter
|
||
|
//
|
||
|
// Synopsis: Checks to see if this is a good time to filter.
|
||
|
//
|
||
|
// Returns: TRUE if okay to filter now.
|
||
|
// FALSE o/w
|
||
|
//
|
||
|
// History: 12-Apr-96 srikants Made a version for user space.
|
||
|
// 10-Dec-97 KyleP Make NoLok version
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CFilterManager::IsGoodTimeToFilter()
|
||
|
{
|
||
|
//
|
||
|
// Don't do any work until the system has booted
|
||
|
//
|
||
|
|
||
|
if ( GetTickCount() < _resman.GetRegParams().GetStartupDelay() )
|
||
|
return FALSE;
|
||
|
|
||
|
LARGE_INTEGER CurrentTime;
|
||
|
|
||
|
NTSTATUS Status = NtQuerySystemTime( &CurrentTime );
|
||
|
|
||
|
if ( NT_SUCCESS(Status) )
|
||
|
{
|
||
|
// Check user activity at 5 s. intervals
|
||
|
if ( CurrentTime.QuadPart - _LastResourceCheck > 5 * 10000000i64 )
|
||
|
{
|
||
|
if ( _resman.IsUserActive(FALSE) )
|
||
|
{
|
||
|
//
|
||
|
// If someone is typing on the keyboard, don't get in the way.
|
||
|
//
|
||
|
|
||
|
_LastResourceStatus = FALSE;
|
||
|
_resman.ReportFilteringState( CIF_STATE_USER_ACTIVE );
|
||
|
return _LastResourceStatus;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ULONG ulDelay = _LastResourceStatus ?
|
||
|
_resman.GetRegParams().GetWordlistResourceCheckInterval() :
|
||
|
_resman.GetRegParams().GetLowResourceSleep();
|
||
|
|
||
|
if ( CurrentTime.QuadPart - _LastResourceCheck > ulDelay * 10000000i64 )
|
||
|
{
|
||
|
_LastResourceCheck = CurrentTime.QuadPart;
|
||
|
|
||
|
DWORD dwFilterState;
|
||
|
|
||
|
if ( _resman.IsMemoryLow() )
|
||
|
{
|
||
|
//
|
||
|
// If we're low on memory, don't eat up more with word lists
|
||
|
//
|
||
|
|
||
|
_LastResourceStatus = FALSE;
|
||
|
dwFilterState = CIF_STATE_LOW_MEMORY;
|
||
|
}
|
||
|
else if ( _resman.IsBatteryLow() )
|
||
|
{
|
||
|
_LastResourceStatus = FALSE;
|
||
|
dwFilterState = CIF_STATE_BATTERY_POWER;
|
||
|
}
|
||
|
else if ( _resman.IsIoHigh() )
|
||
|
{
|
||
|
//
|
||
|
// If someone else is doing a lot of I/O, then we shouldn't add
|
||
|
// to the problem.
|
||
|
//
|
||
|
|
||
|
_LastResourceStatus = FALSE;
|
||
|
dwFilterState = CIF_STATE_HIGH_IO;
|
||
|
}
|
||
|
else if ( _resman.IsUserActive(TRUE) )
|
||
|
{
|
||
|
//
|
||
|
// If someone is typing on the keyboard, don't get in the way.
|
||
|
// This check is done after the Hi I/O check because the user
|
||
|
// activity is sampled over 5 seconds there.
|
||
|
//
|
||
|
|
||
|
_LastResourceStatus = FALSE;
|
||
|
dwFilterState = CIF_STATE_USER_ACTIVE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_LastResourceStatus = TRUE;
|
||
|
dwFilterState = 0;
|
||
|
}
|
||
|
|
||
|
_resman.ReportFilteringState( dwFilterState );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return _LastResourceStatus;
|
||
|
}
|
||
|
|
||
|
#pragma warning( disable : 4756 ) // overflow in constant arithmetic
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::FilterDataReady, public
|
||
|
//
|
||
|
// Synopsis: Adds the contents of entryBuf to the current Word List.
|
||
|
//
|
||
|
// Returns: Whether the word list is full.
|
||
|
//
|
||
|
// Arguments: [pEntryBuf] -- pointer to data to be added to Word List
|
||
|
// [cb] -- count of bytes in buffer
|
||
|
//
|
||
|
// History: 22-Mar-93 AmyA Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterDataReady( const BYTE * pEntryBuf, ULONG cb )
|
||
|
{
|
||
|
if ( _pWordList.IsNull() )
|
||
|
{
|
||
|
ciDebugOut(( DEB_WARN,
|
||
|
"CFilterManager::FilterDataReady. No wordlist active.\n" ));
|
||
|
return FDAEMON_E_NOWORDLIST;
|
||
|
}
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterDataReady.\n" ));
|
||
|
SCODE sc = S_OK;
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
_resman.SampleUserActivity();
|
||
|
|
||
|
while (!_pWordList->MakeChunk( pEntryBuf, cb ))
|
||
|
continue;
|
||
|
|
||
|
if ( _pWordList->Size() >= _resman.GetRegParams().GetMaxWordlistSize() )
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDataReady -- Wordlist full\n" ));
|
||
|
sc = FDAEMON_W_WORDLISTFULL;
|
||
|
}
|
||
|
|
||
|
if ( _resman.IsMemoryLow() )
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDataReady -- Running low on memory\n" ));
|
||
|
sc = FDAEMON_W_WORDLISTFULL;
|
||
|
|
||
|
//
|
||
|
// Try forcing a merge to free things up.
|
||
|
//
|
||
|
|
||
|
_resman.ForceMerge( partidInvalid, CI_ANY_MERGE );
|
||
|
}
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ERROR,
|
||
|
"Exception 0x%x caught during filtering. "
|
||
|
"Rescheduling/aborting documents for filtering.\n",
|
||
|
e.GetErrorCode() ));
|
||
|
if ( e.GetErrorCode() == STATUS_INSUFFICIENT_RESOURCES )
|
||
|
sc = FDAEMON_E_LOWRESOURCE;
|
||
|
else
|
||
|
sc = FDAEMON_E_FATALERROR;
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
for ( unsigned cWid = 0; cWid < _docList.Count(); cWid++ )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ITRACE,
|
||
|
"Reschedule/aborting filtering of wid %d\n",
|
||
|
_docList.Wid(cWid) ));
|
||
|
}
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
_pWordList.Delete();
|
||
|
|
||
|
//
|
||
|
// No refiling in push filtering, hence abort wids. Refile docList
|
||
|
// in pull filtering.
|
||
|
//
|
||
|
if ( _fPushFiltering )
|
||
|
_resman.NoFailAbortWidsInDocList();
|
||
|
else
|
||
|
_resman.NoFailReFile( _resman.GetDocList() );
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE,
|
||
|
"CFilterManager::FilterDataReady "
|
||
|
"deleting wordlist and clearing doclist\n" ));
|
||
|
}
|
||
|
END_CATCH
|
||
|
|
||
|
if ( sc == FDAEMON_E_LOWRESOURCE )
|
||
|
_resman.NoFailFreeResources();
|
||
|
|
||
|
#if CIDBG==1
|
||
|
{
|
||
|
CPriLock lock(_resman.GetMutex());
|
||
|
Win4Assert( 0 == _docList.Count() && _pWordList.IsNull() ||
|
||
|
0 != _docList.Count() && !_pWordList.IsNull() );
|
||
|
}
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::FilterDone, public
|
||
|
//
|
||
|
// Synopsis: Commits the current wordlist.
|
||
|
//
|
||
|
// Arguments: [aStatus] -- array of STATUS for current document list
|
||
|
// [count] -- count for array
|
||
|
//
|
||
|
// History: 22-Mar-93 AmyA Created from MakeWordList
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterDone( const STATUS * aStatus, ULONG count )
|
||
|
{
|
||
|
if ( _pWordList.IsNull() )
|
||
|
{
|
||
|
Win4Assert( 0 == _docList.Count() );
|
||
|
|
||
|
ciDebugOut(( DEB_WARN,
|
||
|
"CFilterManager::FilterDone. No wordlist active.\n" ));
|
||
|
|
||
|
return( FDAEMON_E_NOWORDLIST );
|
||
|
}
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, "# CFilterManager::FilterDone.\n" ));
|
||
|
_resman.SampleUserActivity();
|
||
|
|
||
|
// add statuses to _docList
|
||
|
|
||
|
unsigned cDocuments = _docList.Count();
|
||
|
Win4Assert( cDocuments <= count );
|
||
|
|
||
|
for ( unsigned i = 0; i < cDocuments; i++ )
|
||
|
{
|
||
|
//
|
||
|
// Note - we are modifying the status on the _docList in resman
|
||
|
// without obtaining the resman lock. This is okay as we need a lock
|
||
|
// only to protect the count in doclist.
|
||
|
// Note: retries are 1-based, so the first filter attempt is 1.
|
||
|
//
|
||
|
if ( _docList.Retries(i) > _resman.GetRegParams().GetFilterRetries() )
|
||
|
{
|
||
|
_resman.GetDocList().SetStatus( i, TOO_MANY_RETRIES );
|
||
|
}
|
||
|
else if ( CI_SHARING_VIOLATION == aStatus[i] &&
|
||
|
_docList.SecQRetries(i) >= _resman.GetRegParams().GetSecQFilterRetries() )
|
||
|
{
|
||
|
// Documents are put in sec Q only in case of sharing violation.
|
||
|
// Hence the above check.
|
||
|
|
||
|
ciDebugOut(( DEB_IWARN, "TOO_MANY_SECQ_RETRIES Doc Wid = %ld, SecQRetries = %d\n",
|
||
|
_docList.Wid(i),
|
||
|
_docList.SecQRetries(i) ));
|
||
|
_resman.GetDocList().SetStatus( i, TOO_MANY_SECQ_RETRIES );
|
||
|
}
|
||
|
else if ( _docList.Status(i) != DELETED)
|
||
|
{
|
||
|
if ( _pWordList->IsEmpty() &&
|
||
|
( WL_IGNORE != aStatus[i] &&
|
||
|
WL_NULL != aStatus[i] )
|
||
|
&& CI_SHARING_VIOLATION != aStatus[i] )
|
||
|
{
|
||
|
|
||
|
if ( CI_NOT_REACHABLE == aStatus[i] )
|
||
|
{
|
||
|
ciDebugOut (( DEB_WARN, "Wid %ld is UNREACHABLE\n",
|
||
|
_docList.Wid(i) ));
|
||
|
|
||
|
_resman.GetDocList().SetStatus( i, TOO_MANY_RETRIES );
|
||
|
_resman.MarkUnReachable( _docList.Wid(i) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// If the wordlist is emtpy, it means the filter did not
|
||
|
// give us any data but still called FilterDone. We should
|
||
|
// treat all the documents as "exceptions".
|
||
|
//
|
||
|
|
||
|
ciDebugOut (( DEB_IWARN, "setting %d'th document"
|
||
|
" with wid %ld, status %d to exception status\n",
|
||
|
i, _docList.Wid(i), aStatus[i] ));
|
||
|
|
||
|
//
|
||
|
// Okay not to obtain the lock - only modifying the status.
|
||
|
//
|
||
|
_resman.GetDocList().SetStatus( i, FILTER_EXCEPTION );
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_resman.GetDocList().SetStatus( i, aStatus[i] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
PARTITIONID partid = _docList.PartId();
|
||
|
INDEXID iid = _pWordList->GetId();
|
||
|
|
||
|
SCODE sc = 0;
|
||
|
BOOL fRetryFailures = FALSE;
|
||
|
BOOL fDone = TRUE; // This is so FilterMore doesn't
|
||
|
// get messed up. Eventually, FilterDone
|
||
|
// should be split into FilterDone and
|
||
|
// CommitWordList.
|
||
|
|
||
|
|
||
|
{
|
||
|
//=============================================
|
||
|
CPriLock lock ( _resman.GetMutex() );
|
||
|
// Resman is under lock till the end of block
|
||
|
//=============================================
|
||
|
|
||
|
|
||
|
if ( _resman.IsEmpty() ) // Content index empty
|
||
|
{
|
||
|
_resman.GetDocList().LokClear();
|
||
|
return STATUS_CANCELLED;
|
||
|
}
|
||
|
|
||
|
// Special cases:
|
||
|
// 1. partition has been deleted
|
||
|
// - entries in changes and fresh have been removed
|
||
|
// - delete wordlist and return
|
||
|
// 2. wordlist is empty
|
||
|
// - delete wordlist
|
||
|
// - update changes
|
||
|
// 3. no available index id's
|
||
|
// - put docList back into changes
|
||
|
// - delete word list
|
||
|
// - force merge
|
||
|
// 4. no success statuses
|
||
|
// - delete word list
|
||
|
|
||
|
iid = _resman.LokMakeWordlistId (partid);
|
||
|
|
||
|
if (iid == iidInvalid)
|
||
|
{
|
||
|
ciDebugOut (( DEB_IWARN, "Invalid partition, Deleting wordlist\n" ));
|
||
|
|
||
|
_pWordList.Delete();
|
||
|
_resman.GetDocList().LokClear();
|
||
|
|
||
|
return( FDAEMON_E_PARTITIONDELETED );
|
||
|
}
|
||
|
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
|
||
|
"new wordlist id %lx\n", iid ));
|
||
|
|
||
|
|
||
|
Win4Assert( iid != iidInvalid );
|
||
|
|
||
|
_pWordList->SetId ( iid );
|
||
|
|
||
|
BOOL successes = FALSE;
|
||
|
for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
|
||
|
{
|
||
|
switch ( _docList.Status(iDoc) )
|
||
|
{
|
||
|
case TOO_MANY_RETRIES:
|
||
|
case TOO_MANY_SECQ_RETRIES:
|
||
|
if ( !fRetryFailures )
|
||
|
{
|
||
|
//
|
||
|
// Save the state of the retry failures in the retry
|
||
|
// fail list before they are overwritten.
|
||
|
//
|
||
|
_retryFailList = _docList;
|
||
|
fRetryFailures = TRUE;
|
||
|
}
|
||
|
// NOTE: fall through
|
||
|
case TOO_MANY_BLOCKS_TO_FILTER:
|
||
|
successes = TRUE;
|
||
|
_pWordList->MarkWidUnfiltered(iDoc);
|
||
|
_resman.GetDocList().SetStatus( iDoc, SUCCESS );
|
||
|
break;
|
||
|
|
||
|
case SUCCESS:
|
||
|
_resman.IncrementFilteredDocumentCount();
|
||
|
successes = TRUE;
|
||
|
break;
|
||
|
|
||
|
case CI_SHARING_VIOLATION:
|
||
|
//
|
||
|
// If push filtering, abort the wid. If pull filtering,
|
||
|
// add the wid to the sec queue so that filtering can be
|
||
|
// re-attempted later.
|
||
|
//
|
||
|
if ( _fPushFiltering )
|
||
|
_resman.NoFailAbortWid( _docList.Wid(iDoc), _docList.Usn(iDoc) );
|
||
|
else
|
||
|
{
|
||
|
_resman.GetDocList().LokIncrementSecQRetryCount(iDoc);
|
||
|
_resman.LokAddToSecQueue( partid,
|
||
|
_docList.Wid(iDoc),
|
||
|
_docList.VolumeId( iDoc ),
|
||
|
_docList.SecQRetries( iDoc ) );
|
||
|
}
|
||
|
|
||
|
_resman.GetDocList().SetStatus( iDoc, WL_NULL );
|
||
|
|
||
|
// WL_NULL is returned when the filter deliberately does
|
||
|
// not return any data. Like DELETED, but we need to also
|
||
|
// delete any existing data in the index.
|
||
|
case WL_NULL:
|
||
|
_pWordList->DeleteWidData(iDoc);
|
||
|
// NOTE: fall through
|
||
|
case DELETED:
|
||
|
successes = TRUE;
|
||
|
break;
|
||
|
|
||
|
case WL_IGNORE:
|
||
|
_pWordList->DeleteWidData(iDoc);
|
||
|
if ( iDoc == cDocuments - 1 )
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, ">> Not all filtered\n" ));
|
||
|
fDone = FALSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case PENDING:
|
||
|
Win4Assert( !"Ci Daemon Still returning Pending" );
|
||
|
//
|
||
|
// NOTE: Fall through is deliberate - should remove PENDING
|
||
|
// when we find out we don't need it.
|
||
|
//
|
||
|
case PREEMPTED:
|
||
|
//
|
||
|
// NOTE: Fall through is deliberate.
|
||
|
//
|
||
|
default:
|
||
|
ciDebugOut (( DEB_ITRACE,"++++ %d'th document"
|
||
|
" with fake wid %d and wid %ld failed: status %d\n",
|
||
|
iDoc, iDocToFakeWid(iDoc),
|
||
|
_docList.Wid(iDoc), _docList.Status(iDoc) ));
|
||
|
|
||
|
|
||
|
//
|
||
|
// something went wrong during filtering. Delete all data
|
||
|
// associated with this workid.
|
||
|
//
|
||
|
_resman.GetDocList().LokIncrementRetryCount(iDoc);
|
||
|
_pWordList->DeleteWidData(iDoc);
|
||
|
|
||
|
Win4Assert( _docList.Retries(iDoc) <= (_resman.GetRegParams().GetFilterRetries()+1) );
|
||
|
|
||
|
//
|
||
|
// If push filtering abort the wid. If pull filtering add it back
|
||
|
// to the change log for re-filtering at a later time.
|
||
|
//
|
||
|
if ( _fPushFiltering )
|
||
|
_resman.NoFailAbortWid( _docList.Wid(iDoc), _docList.Usn(iDoc) );
|
||
|
else
|
||
|
_resman.LokReFileDocument( partid,
|
||
|
_docList.Wid(iDoc),
|
||
|
_docList.Usn(iDoc),
|
||
|
_docList.VolumeId(iDoc),
|
||
|
_docList.Retries(iDoc),
|
||
|
_docList.SecQRetries(iDoc) );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( !successes ) // only invalid data in word list
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
|
||
|
"deleting wordlist with invalid data\n" ));
|
||
|
_pWordList.Delete();
|
||
|
|
||
|
sc = FDAEMON_W_EMPTYWORDLIST;
|
||
|
}
|
||
|
else if ( !_resman.LokTransferWordlist ( _pWordList ) )
|
||
|
{
|
||
|
//
|
||
|
// If push filtering, abort all wids. If pull filtering,
|
||
|
// refile all wids.
|
||
|
//
|
||
|
if ( _fPushFiltering)
|
||
|
_resman.NoFailAbortWidsInDocList();
|
||
|
else
|
||
|
_resman.NoFailReFile( _resman.GetDocList() );
|
||
|
|
||
|
_pWordList.Delete();
|
||
|
|
||
|
return( FDAEMON_E_WORDLISTCOMMITFAILED );
|
||
|
}
|
||
|
|
||
|
if ( fDone && (WL_IGNORE != aStatus[cDocuments-1]) )
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterDone "
|
||
|
"clearing doclist\n" ));
|
||
|
_resman.GetDocList().LokClear();
|
||
|
}
|
||
|
|
||
|
_resman.LokUpdateCounters(); // Update # filtred documents
|
||
|
}
|
||
|
//==========================================
|
||
|
|
||
|
//
|
||
|
// Notification of failed docs cannot be done under lock.
|
||
|
//
|
||
|
if ( fRetryFailures )
|
||
|
{
|
||
|
for ( unsigned iDoc = 0; iDoc < cDocuments; iDoc++ )
|
||
|
{
|
||
|
if ( ( _retryFailList.Status(iDoc) == TOO_MANY_RETRIES ||
|
||
|
_retryFailList.Status(iDoc) == TOO_MANY_SECQ_RETRIES ) &&
|
||
|
CI_NOT_REACHABLE != aStatus[iDoc] )
|
||
|
_resman.ReportFilterFailure (_retryFailList.Wid(iDoc));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::StoreValue
|
||
|
//
|
||
|
// Synopsis: Store a property value.
|
||
|
//
|
||
|
// Arguments: [widFake] -- Fake wid of object.
|
||
|
// [prop] -- Property descriptor
|
||
|
// [var] -- Value
|
||
|
// [fCanStore] -- Returns TRUE if this is a storable property
|
||
|
//
|
||
|
// History: 21-Dec-95 KyleP Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterStoreValue( WORKID widFake,
|
||
|
CFullPropSpec const & ps,
|
||
|
CStorageVariant const & var,
|
||
|
BOOL & fCanStore )
|
||
|
{
|
||
|
fCanStore = FALSE;
|
||
|
|
||
|
Win4Assert( widFake > 0 );
|
||
|
|
||
|
if ( widFake <= 0 )
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
widFake = FakeWidToIndex( widFake );
|
||
|
|
||
|
if ( widFake >= _docList.Count() )
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
return _resman.StoreValue( _docList.Wid( widFake ), ps, var, fCanStore );
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::StoreSecurity
|
||
|
//
|
||
|
// Synopsis: Store a file's security descriptor.
|
||
|
//
|
||
|
// Arguments: [widFake] -- Fake wid of object.
|
||
|
// [pSD] -- pointer to security descriptor
|
||
|
// [cbSD] -- size in bytes of pSD
|
||
|
// [fCanStore] -- Returns TRUE if storae succeeded
|
||
|
//
|
||
|
// Notes: Used only for downlevel index
|
||
|
//
|
||
|
// History: 21-Dec-95 KyleP Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterStoreSecurity( WORKID widFake,
|
||
|
PSECURITY_DESCRIPTOR pSD,
|
||
|
ULONG cbSD,
|
||
|
BOOL & fCanStore )
|
||
|
{
|
||
|
fCanStore = FALSE;
|
||
|
|
||
|
Win4Assert( widFake > 0 );
|
||
|
|
||
|
if ( widFake <= 0 )
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
widFake = FakeWidToIndex( widFake );
|
||
|
|
||
|
if ( widFake >= _docList.Count() )
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
fCanStore = _resman.StoreSecurity( _docList.Wid( widFake ), pSD, cbSD );
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::FPSToPROPID, public
|
||
|
//
|
||
|
// Synopsis: Converts FULLPROPSPEC property to PROPID
|
||
|
//
|
||
|
// Arguments: [fps] -- FULLPROPSPEC representation of property
|
||
|
// [pid] -- PROPID written here on success
|
||
|
//
|
||
|
// Returns: S_OK on success
|
||
|
//
|
||
|
// History: 29-Dec-1997 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FPSToPROPID( CFullPropSpec const & fps, PROPID & pid )
|
||
|
{
|
||
|
return _resman.FPSToPROPID( fps, pid );
|
||
|
}
|
||
|
|
||
|
#pragma warning( default : 4756 ) // overflow in constant arithmetic
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::FilterMore, public
|
||
|
//
|
||
|
// Synopsis: Commits the current wordlist and creates a new one.
|
||
|
//
|
||
|
// Arguments: [aStatus] -- array of STATUS for the current document list
|
||
|
// [count] -- count for array
|
||
|
//
|
||
|
// History: 03-May-93 AmyA Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
SCODE CFilterManager::FilterMore( const STATUS * aStatus, ULONG count )
|
||
|
{
|
||
|
ciDebugOut (( DEB_ITRACE, "# CFilterManager::FilterMore\n" ));
|
||
|
if ( _pWordList.IsNull() )
|
||
|
{
|
||
|
ciDebugOut(( DEB_WARN,
|
||
|
"FilterMore: No wordlist active.\n" ));
|
||
|
return( FDAEMON_E_NOWORDLIST );
|
||
|
}
|
||
|
|
||
|
SCODE sc = FilterDone( aStatus, count ); // Commits Word List
|
||
|
|
||
|
if ( SUCCEEDED(sc) ) // No problems occured
|
||
|
{
|
||
|
// _docList may have been cleared for an error condition.
|
||
|
ciDebugOut (( DEB_ITRACE, "CFilterManager::FilterMore "
|
||
|
"_docList.Count() = %d\n", _docList.Count() ));
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
ciFAILTEST(STATUS_NO_MEMORY);
|
||
|
if ( _docList.Count() != 0 )
|
||
|
NewWordList(); // Creates New Word List
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
sc = e.GetErrorCode();
|
||
|
|
||
|
Win4Assert( _pWordList.IsNull() );
|
||
|
|
||
|
//
|
||
|
// If push filtering, abort wids. In pull filtering add these documents
|
||
|
// back to the change log so that they will get filtered later.
|
||
|
//
|
||
|
if ( _fPushFiltering )
|
||
|
_resman.NoFailAbortWidsInDocList();
|
||
|
else
|
||
|
{
|
||
|
if (!_resman.NoFailReFile ( _resman.GetDocList() ))
|
||
|
sc = STATUS_CANCELLED;
|
||
|
}
|
||
|
}
|
||
|
END_CATCH
|
||
|
}
|
||
|
|
||
|
#if CIDBG==1
|
||
|
{
|
||
|
CPriLock lock(_resman.GetMutex());
|
||
|
Win4Assert( 0 == _docList.Count() && _pWordList.IsNull() ||
|
||
|
0 != _docList.Count() && !_pWordList.IsNull() );
|
||
|
}
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
return sc;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CFilterManager::NewWordList, public
|
||
|
//
|
||
|
// Synopsis: Creates word list
|
||
|
//
|
||
|
// History: 08-Apr-91 BartoszM Created
|
||
|
// 24-Nov-92 KyleP Retry pending documents
|
||
|
// 22-Mar-93 AmyA Split into NewWordList and
|
||
|
// CommitWordList
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
void CFilterManager::NewWordList()
|
||
|
{
|
||
|
unsigned cDocuments = _docList.Count();
|
||
|
Win4Assert ( cDocuments != 0 );
|
||
|
PARTITIONID partid = _docList.PartId();
|
||
|
|
||
|
|
||
|
WORKID widMax = _docList.WidMax();
|
||
|
CIndexId iid ( iidInvalid, partid );
|
||
|
|
||
|
ciDebugOut (( DEB_ITRACE, "NewWordlist. Temporary iid %lx\n", iid ));
|
||
|
|
||
|
_pWordList.Initialize ( iid, widMax );
|
||
|
|
||
|
// initialize the wid translation table
|
||
|
|
||
|
for ( unsigned i = 0; i < cDocuments; i++ )
|
||
|
_pWordList->AddWid( _docList.Wid(i), _docList.VolumeId(i) );
|
||
|
}
|