windows-nt/Source/XPSP1/NT/inetsrv/query/cindex/ci.cxx
2020-09-26 16:20:57 +08:00

1804 lines
53 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: CI.CXX
//
// Contents: Main Content Index class methods
//
// Classes: CContentIndex - Main content index class
// CCI - Exports content index
//
// History: 21-Aug-91 KyleP Added CCI
// 26-Feb-91 KyleP Created stubs
// 25-Mar-91 BartoszM Implemented
// 03-Nov-93 w-PatG Added KeyList methods
// 03-Jan-95 BartoszM Separated Filter Manager
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <cci.hxx>
#include <curstk.hxx>
#include <convert.hxx>
#include <qparse.hxx>
#include <heap.hxx>
#include <pidmap.hxx>
#include <pidxtbl.hxx>
#include <eventlog.hxx>
#include <cievtmsg.h>
#include "ci.hxx"
#include "partn.hxx"
#include "fretest.hxx"
#include "freshcur.hxx"
#include "unioncur.hxx"
#include "widarr.hxx"
#include "index.hxx"
#if CIDBG == 1
void SetGlobalInfoLevel ( ULONG level )
{
_SetWin4InfoLevel(level);
}
#endif // CIDBG == 1
#define AssertOnNotFound(status) Win4Assert((status) != STATUS_NOT_FOUND)
ULONG ciDebugGlobalFlags = 0;
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::CContentIndex, public
//
// Synopsis: Content index constructor.
//
// Effects: Creates a content index object.
//
// Arguments: [storage] -- Storage object
// [xact] -- startup transaction
// [xIndexNotifTable] -- Table of notifications in push filtering
//
// History: 21-Aug-91 KyleP Removed unused path member
// 26-Feb-91 KyleP Created.
// 01-Apr-91 BartoszM Implemented
//
//----------------------------------------------------------------------------
CContentIndex::CContentIndex( PStorage &storage,
CCiFrameworkParams & params,
ICiCDocStore * pICiCDocStore,
CI_STARTUP_INFO const & startupInfo,
IPropertyMapper * pIPropertyMapper,
CTransaction& xact,
XInterface<CIndexNotificationTable> & xIndexNotifTable )
: _storage (storage),
_resman ( storage,
params,
pICiCDocStore,
startupInfo,
pIPropertyMapper,
xact,
xIndexNotifTable ),
_filterMan ( _resman )
{
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::ReserveUpdate, public
//
// Synopsis: Reserves slot for pending update.
//
// Arguments: [wid] -- work id. Used for confirmation of hint.
//
// Returns: Hint. Used in CConentIndex::Update to speed processing.
//
// History: 30-Aug-95 KyleP Created
//
//----------------------------------------------------------------------------
unsigned CContentIndex::ReserveUpdate( WORKID wid )
{
return _resman.ReserveUpdate( wid );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::Update, public
//
// Synopsis: Notifies the content index of document update/deletion.
//
// Effects: Schedules document for
// indexing. This method is also called to add new documents
// (a special case of update).
//
// Arguments: [iHint] -- Cookie representing unique position in queue.
// [wid] -- work id
// [partid] -- Partition id
// [usn] -- unique sequence number
// [volumeId] -- Volume id
// [action] -- update/delete
//
// History: 08-Oct-93 BartoszM Created
//
//----------------------------------------------------------------------------
SCODE CContentIndex::Update( unsigned iHint,
WORKID wid,
PARTITIONID partid,
USN usn,
VOLUMEID volumeId,
ULONG action )
{
return _resman.UpdateDocument ( iHint, wid, partid, usn, volumeId, action );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::Query, public
//
// Synopsis: Resolves a free-text query.
//
// Effects: Parses a free text query and creates a tree of cursors to
// resolve the query.
//
// Arguments: [pRst] -- Pointer to tree of query restriction(s)
//
// [pFlags] -- Holds information about the status of the query
//
// [cPartitions] -- The number of partitions in aPartID.
//
// [aPartID] -- Array of partitionIDs specifying partitions
// to be searched.
//
// [cPendingUpdates] -- A non-zero value here will return pending
// updates in addition to whatever values match the query.
// If more than cPendingUpdates exist in any single partition,
// then the CI_NOT_UP_TO_DATE flag will be set.
//
// [cMaxNodes] -- Maximum expansion for query. Roughly counted
// in terms of leaf query nodes.
//
// Requires: All partitions in aPartID are valid.
//
// Returns: A cursor to iterate over the results
//
// Modifies: pFlags will be modified to reflect the status of the query
//
// History: 19-Sep-91 BartoszM Created.
// 08-Sep-92 AmyA Added pFlags functionality
//
//----------------------------------------------------------------------------
CCursor* CContentIndex::Query( CRestriction const * pRst,
ULONG* pFlags,
UINT cPartitions,
PARTITIONID aPartId[],
ULONG cPendingUpdates,
ULONG cMaxNodes )
{
CCursor* pCurResult = 0;
ciDebugOut (( DEB_ITRACE, "CContentIndex::Query\n" ));
*pFlags = 0; // any incoming information should have been obtained already
if ( pRst == 0 )
{
*pFlags |= CI_NOISE_PHRASE;
}
//
// Acquire resources from resman
//
CIndexSnapshot indSnap ( _resman );
indSnap.Init ( cPartitions, aPartId, cPendingUpdates, pFlags );
unsigned cInd = indSnap.Count();
CCurStack curStack(cInd);
//
// If all the cursors against all persistent indexes only have unfiltered files,
// mark the resulting cursor as only containing unfiltered files.
//
BOOL fUnfilteredOnly = TRUE;
for ( unsigned i = 0; i < cInd; i++ )
{
CConverter convert ( indSnap.Get(i), cMaxNodes );
XCursor xCur( convert.QueryCursor ( pRst ) );
if ( !xCur.IsNull() )
{
if( !xCur->IsUnfilteredOnly() )
{
fUnfilteredOnly = FALSE;
}
curStack.Push( xCur.GetPointer() );
xCur.Acquire();
}
else if ( convert.TooManyNodes() )
{
*pFlags |= CI_TOO_MANY_NODES;
return 0;
}
}
XCursor curResult;
unsigned cCur = curStack.Count();
ciDebugOut((DEB_FILTERWIDS,
"cCur == %d, indSnap.Count() == %d\n",
cCur, indSnap.Count() ));
if ( cCur > 1 )
{
curResult.Set( new CUnionCursor ( cCur, curStack ) );
}
else if ( cCur == 1 )
{
curResult.Set( curStack.Pop() );
}
// Acquires resources from index snapshot
if ( !curResult.IsNull() )
{
curResult.Set( new CFreshCursor ( curResult, indSnap ) );
//
// OR in cursors for pending updates, but only if we have some real updates
// to augment. Performing this action in the absence of real updates will
// make a query over an unindexed property appear to be indexed.
//
if ( cPendingUpdates > 0 )
{
indSnap.AppendPendingUpdates( curResult );
Win4Assert( curStack.Count() == 0 );
}
curResult->SetUnfilteredOnly( fUnfilteredOnly );
}
return( curResult.Acquire() );
}
NTSTATUS CContentIndex::Dismount()
{
_resman.Dismount();
return _filterMan.Dismount();
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::FilterReady, public
//
// Synopsis: Retrieves list of documents to be filtered. Blocks thread if
// resources are not available or if there are no documents to
// be filtered.
//
// Arguments: [docBuffer] -- (in, out) buffer for paths and properties of
// documents to be filtered.
// [cwc] -- (in) count of WCHAR's in docBuffer
// [cMaxDocs] -- (in) the maximum number of docs that can be
// filtered at once by the filter daemon.
//
// History: 18-Apr-93 AmyA Created.
//
//----------------------------------------------------------------------------
SCODE CContentIndex::FilterReady ( BYTE * docBuffer,
ULONG & cb,
ULONG cMaxDocs )
{
return _filterMan.FilterReady( docBuffer, cb, cMaxDocs );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::FilterDataReady, public
//
// Synopsis: Adds the contents of entry buffer to the current word list.
//
// Returns: Whether 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 CContentIndex::FilterDataReady( const BYTE * pEntryBuf, ULONG cb )
{
return _filterMan.FilterDataReady( pEntryBuf, cb );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::FilterMore, public
//
// Synopsis: Commits the current word list and initializes a new one.
//
// Arguments: [aStatus] -- array of STATUS for the resource manager
// [count] -- count for array
//
// History: 03-May-93 AmyA Created.
//
//----------------------------------------------------------------------------
SCODE CContentIndex::FilterMore( const STATUS * aStatus, ULONG count )
{
return( _filterMan.FilterMore( aStatus, count ) );
}
SCODE CContentIndex::FilterStoreValue(
WORKID widFake,
CFullPropSpec const & ps,
CStorageVariant const & var,
BOOL & fStored )
{
return _filterMan.FilterStoreValue( widFake, ps, var, fStored );
}
SCODE CContentIndex::FilterStoreSecurity(
WORKID widFake,
PSECURITY_DESCRIPTOR pSD,
ULONG cbSD,
BOOL & fStored )
{
return _filterMan.FilterStoreSecurity( widFake, pSD, cbSD, fStored );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::FilterDone, public
//
// Synopsis: Commits the current word list.
//
// Arguments: [aStatus] -- array of STATUS for the resource manager
// [count] -- count for array
//
// History: 26-Mar-93 AmyA Created.
//
//----------------------------------------------------------------------------
SCODE CContentIndex::FilterDone( const STATUS * aStatus, ULONG count )
{
return( _filterMan.FilterDone( aStatus, count ) );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::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 CContentIndex::FPSToPROPID( CFullPropSpec const & fps, PROPID & pid )
{
return _filterMan.FPSToPROPID( fps, pid );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::CreatePartition, public
//
// Synopsis: Create a new partition in the content index.
//
// Effects: Stores persistent information about the new partition and
// creates a new partition object.
//
// Arguments: [partID] -- ID of new partition. Further references to the new
// partition must use [partID].
//
// Requires: [partID] is not the ID of any existing partition.
//
// Signals: CPartitionException
//
// History: 26-Feb-91 KyleP Created.
//
//----------------------------------------------------------------------------
void CContentIndex::CreatePartition( PARTITIONID partID )
{
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::DestroyPartition, public
//
// Synopsis: Destroys a content index partition and all data within it.
//
// Effects: This is a very powerful call. All document data stored in
// the destroyed partition is permanently lost. The MergePartitions
// method is a more common method of getting rid of a partition.
//
// Arguments: [partID] -- ID of partition to be destroyed.
//
// Signals: CPartitionException
//
// History: 26-Feb-91 KyleP Created.
//
//----------------------------------------------------------------------------
void CContentIndex::DestroyPartition(PARTITIONID partID)
{
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::MergePartitions, public
//
// Synopsis: Merges one partition into another.
//
// Effects: Merges all documents in [partSrc] into [partDest] and then
// deletes [partSrc]. Note that although the merge may be
// carried out asyncronously the effects of the merge are
// immediately visible (references to [partDest] will map to
// [partDest] and [partSrc]).
//
// Arguments: [partDest] -- Partition into which [partSrc] is merged.
//
// [partSrc] -- Partition which is merged into [partDest].
//
// Requires: [partDest] and [partSrc] exist.
//
// Signals: CPartitionException
//
// Modifies: [partSrc] is no longer a valid reference in the content index.
//
// Notes: From the point of view of the content index, a partition is just
// a collection of documents. The translation of partitions to
// filesystem scope is performed at a higher level. Thus any
// partition manipulation more complicated than a merge (such as
// changing the scope of a partition) must be performed at a
// higher level.
//
// The content index must be notified about documents which move
// from one partition to another as the result of rescoping but the
// scope change must be translated at a higher level into
// DeleteDocument and UpdateDocument calls.
//
// History: 26-Feb-91 KyleP Created.
//
//----------------------------------------------------------------------------
void CContentIndex::MergePartitions(
PARTITIONID partDest,
PARTITIONID partSrc)
{
}
#if CIDBG == 1
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::DebugStats, public
//
// Synopsis: Returns statistics on the use and composition of the CI.
//
// Arguments: [pciDebStats] -- Pointer to debug statistics structure.
//
// Modifies: The structure pointed at by pciDebStats is filled in with
// the statistical information.
//
// History: 26-Feb-91 KyleP Created.
//
// Notes: This function is only avaliable in the debug version of the
// content index.
//
//----------------------------------------------------------------------------
void CContentIndex::DebugStats(/* CIDEBSTATS *pciDebStats */)
{
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::DebugDump, public
//
// Synopsis: Dumps a plain text representation of the content index.
//
// Arguments: [sOut] -- Output stream. A formatted representation of the
// content index is sent here.
//
// Modifies: Text is written to [sOut].
//
// History: 26-Feb-91 KyleP Created.
//
// Notes: This function is only avaliable in the debug version of the
// content index.
//
//----------------------------------------------------------------------------
void CContentIndex::DumpIndexes( FILE * pf, INDEXID iid, ULONG fSummaryOnly )
{
PARTITIONID partid = 1;
CFreshTest * pfresh;
unsigned cind;
ULONG pFlags = 0;
CIndex ** ppIndex =
_resman.QueryIndexes( 1, // One partition
&partid, // The partition
&pfresh, // Fresh test (unused)
cind, // # indexes.
0, // # pending updates
0, // curPending
&pFlags ); // Still changes to do?
for (int i = cind-1; i >= 0; i-- )
{
if ( 0 == iid || ppIndex[i]->GetId() == iid )
{
fprintf( pf, "\n\nIndex %lx:\n", ppIndex[i]->GetId() );
ppIndex[i]->DebugDump( pf, fSummaryOnly );
}
}
_resman.ReleaseIndexes( cind, ppIndex, pfresh );
delete ppIndex;
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::DumpWorkId, public
//
// Synopsis: Dumps a plain text representation of the content index for
// a single wid.
//
// Arguments: [pb] -- Buffer has bookmark on input, text on output.
// [cb] -- Size of [pb]
//
// History: 30-Mar-95 KyleP Created.
//
// Notes: Calling this API is a little convoluted. Because it is
// called via FSCTL for the kernel implementation, just
// passing one huge buffer is difficult. Instead, each
// buffer of text is terminated with either a null dword
// indicating completion or a bookmark of size sizeof(ULONG) +
// sizeof(WORKID) + sizeof(CKeyBuf). The initial ulong is
// the control code to CContentIndex::Backdoor, followed
// by the workid we're searching for, followed by the
// key we should start up on.
//
//----------------------------------------------------------------------------
void CContentIndex::DumpWorkId( BYTE * pb, ULONG cb )
{
//
// Set up for iteration.
//
BYTE * pbTemp = pb;
pbTemp += sizeof(ULONG); // Skip command
WORKID widLocate = *(WORKID UNALIGNED *)pbTemp;
pbTemp += sizeof(WORKID); // Skip workid
ULONG iidRequested = *(WORKID UNALIGNED *) pbTemp;
pbTemp += sizeof(ULONG);
//
// This copy is "safe" because I know CKeyBuf doesn't contain
// any data members that are pointers.
//
CKeyBuf keyStart;
memcpy( &keyStart, pbTemp, sizeof(keyStart) );
CKey * pkeyStart = 0;
if ( keyStart.Count() != 0 ) // On initial call, count is zero
pkeyStart = new CKey( keyStart );
//
// Compute space to write text. Save enough to write bookmark (and "...\n")
//
char const szContinued[] = "... \n";
char * psz = (char *)pb;
int cc = cb - sizeof(ULONG) - sizeof(WORKID) - sizeof(CKeyBuf) - sizeof(szContinued);
if ( cc <= 0 )
{
ciDebugOut(( DEB_ERROR,
"Buffer passed to CContentIndex::DumpWorkId too small for bookmark!\n" ));
return;
}
//
// Snapshot indices
//
PARTITIONID partid = 1;
CFreshTest * pfresh;
unsigned cind;
ULONG pFlags = 0;
CIndex ** ppIndex = _resman.QueryIndexes( 1, // One partition
&partid, // The partition
&pfresh, // Fresh test (unused)
cind, // # indexes.
0, // # pending updates
0, // curPending
&pFlags ); // Still changes to do?
//
// Loop through indices, looking for freshest.
//
int cbWritten = 0;
for ( int i = cind-1; i >= 0; i-- )
{
if ( ( (0 == iidRequested) && pfresh->IsCorrectIndex( ppIndex[i]->GetId(), widLocate ) ) ||
( ppIndex[i]->GetId() == iidRequested )
)
{
//
// Header
//
if ( pkeyStart == 0 )
{
cbWritten = _snprintf( psz,
cc,
"Dump of wid %d (0x%x)\n"
"Using index 0x%x\n\n",
widLocate,
widLocate,
ppIndex[i]->GetId() );
if ( cbWritten != -1 )
{
psz += cbWritten;
cc -= cbWritten;
}
}
//
// pszStart is where we started writing the last key. A key must
// be written completely or we will try again in the next pass.
//
char * pszStart = psz;
//
// Get cursor, either from beginning or from bookmark.
//
CKeyCursor * pcur;
CKeyBuf const * pkey = 0;
if ( pkeyStart == 0 )
{
pcur = ppIndex[i]->QueryCursor();
pkeyStart = new CKey( *pcur->GetKey() );
}
else
pcur = ppIndex[i]->QueryKeyCursor( pkeyStart );
//
// I think pcur can be null, if indexes / keys shifted beneath us.
//
if ( pcur != 0 )
{
ciDebugOut(( DEB_ITRACE, "Scan on letter: " ));
WCHAR FirstLetter = '@';
for ( pkey = pcur->GetKey();
cbWritten != -1 && pkey != 0;
pkey = pcur->GetNextKey() )
{
if ( *(pkey->GetStr()) != FirstLetter )
{
FirstLetter = *(pkey->GetStr());
ciDebugOut (( DEB_NOCOMPNAME | DEB_ITRACE, "%c", FirstLetter ));
}
//
// Search for specified wid.
//
for ( WORKID wid = pcur->WorkId(); wid < widLocate; wid = pcur->NextWorkId() )
continue;
//
// Print data for the key.
//
if ( wid == widLocate )
{
cbWritten = _snprintf( psz, cc, "Key: %.*ws -- (PID = %lx)\n",
pkey->StrLen(), pkey->GetStr(), pkey->Pid() );
if ( cbWritten != -1 )
{
psz += cbWritten;
cc -= cbWritten;
}
int i = 1;
for ( OCCURRENCE occ = pcur->Occurrence();
cbWritten != 0xFFFFFFFF && occ != OCC_INVALID;
occ = pcur->NextOccurrence(), i++ )
{
cbWritten = _snprintf( psz, cc, " %6ld", occ );
if ( cbWritten != -1 )
{
psz += cbWritten;
cc -= cbWritten;
}
if ( i % 10 == 0 )
{
cbWritten = _snprintf( psz, cc, "\n" );
if ( cbWritten != -1 )
{
psz += cbWritten;
cc -= cbWritten;
}
}
}
if ( cbWritten != -1 )
cbWritten = _snprintf( psz, cc, "\n" );
if ( cbWritten != -1 )
{
psz += cbWritten;
cc -= cbWritten;
pszStart = psz;
}
else
break;
}
} // for
delete pcur;
} // pcur != 0
//
// What if we didn't write any key?
//
if ( pkey && pkey->Compare( *pkeyStart ) == 0 )
{
cbWritten = _snprintf( psz, sizeof(szContinued), szContinued);
psz += cbWritten;
pkey = pcur->GetNextKey();
}
else
{
psz = pszStart;
}
*psz = 0;
psz++; // For final null
//
// Either buffer is full, or we ran out of keys.
//
if ( pkey != 0 )
{
//
// Write bookmark
//
*((ULONG UNALIGNED *)psz) = CiDumpWorkId;
psz += sizeof(ULONG);
*((WORKID UNALIGNED *)psz) = widLocate;
psz += sizeof(WORKID);
*((ULONG UNALIGNED *)psz) = iidRequested;
psz += sizeof(ULONG);
keyStart = *pkey;
memcpy( psz, &keyStart, sizeof(keyStart) );
}
else
{
//
// Write end-of-iteration signature
//
*((ULONG UNALIGNED *)psz) = 0;
}
break;
}
}
//
// We may not have found any data.
//
if ( i < 0 )
{
unsigned cbWritten = _snprintf( psz, cc, "No index contains data for wid %d (0x%x)\n", widLocate, widLocate );
psz += cbWritten;
cc -= cbWritten;
psz++; // For final null
*((ULONG UNALIGNED *)psz) = 0;
}
//
// Cleanup
//
delete pkeyStart;
_resman.ReleaseIndexes( cind, ppIndex, pfresh );
delete ppIndex;
}
#endif // CIDBG == 1
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::KeyToId, public
//
// Synopsis: Maps from a key to an id.
//
// Arguments: [pkey] -- pointer to the key to be mapped to ULONG
//
// Returns: key id - a ULONG
//
// History: 03-Nov-93 w-Patg Created.
//
//----------------------------------------------------------------------------
ULONG CContentIndex::KeyToId( CKey const * pkey )
{
return _resman.KeyToId( pkey );
}
//+---------------------------------------------------------------------------
//
// Member: CContentIndex::IdToKey, public
//
// Synopsis: Maps from an id to a key.
//
// Arguments: [ulKid] -- key id to be mapped to a key
// [rkey] -- reference to the returned key
//
// Returns: void
//
// History: 03-Nov-93 w-Patg Created.
//
//----------------------------------------------------------------------------
void CContentIndex::IdToKey( ULONG ulKid, CKey & rkey )
{
_resman.IdToKey( ulKid, rkey );
return;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::CCI, public
//
// Synopsis: Creates a content index object.
//
// Arguments: [storage] -- Storage
// [xIndexNotifTable] -- Table of notifications in push filtering
//
// History: 21-Aug-91 KyleP Created.
//
//----------------------------------------------------------------------------
CCI::CCI( PStorage & storage,
CCiFrameworkParams & params,
ICiCDocStore * pICiCDocStore,
CI_STARTUP_INFO const & startupInfo,
IPropertyMapper * pIPropertyMapper,
XInterface<CIndexNotificationTable> & xIndexNotifTable )
: _storage(storage),
_pci(0),
_createStatus(STATUS_INTERNAL_ERROR)
{
Win4Assert( 0 != pIPropertyMapper );
if ( startupInfo.startupFlags & CI_CONFIG_EMPTY_DATA )
{
ciDebugOut(( DEB_WARN,
"**** CI_CONFIG_EMPTY_DATA is true. Emptying contents of contentIndex.\n" ));
Empty();
}
_xPropMapper.Set( pIPropertyMapper );
pIPropertyMapper->AddRef();
_createStatus = Create( pICiCDocStore, startupInfo, params, xIndexNotifTable );
if ( STATUS_SUCCESS != _createStatus )
THROW( CException( _createStatus ) );
}
//+---------------------------------------------------------------------------
//
// Member: CCI::~CCI, public
//
// Synopsis: Destroys the content index.
//
// Signals: ???
//
// History: 21-Aug-91 KyleP Created.
//
//----------------------------------------------------------------------------
CCI::~CCI()
{
delete _pci;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::IsCiCorrupt, public
//
// Synopsis: Returns TRUE if the index is corrupt or FALSE otherwise
//
// History: 5-Oct-98 dlee Created.
//
//----------------------------------------------------------------------------
BOOL CCI::IsCiCorrupt()
{
if ( 0 == _pci )
return FALSE;
return _pci->IsCiCorrupt();
} //IsCiCorrupt
SCODE CCI::FilterStoreValue(
WORKID widFake,
CFullPropSpec const & ps,
CStorageVariant const & var,
BOOL & fStored )
{
fStored = FALSE;
if ( 0 != _pci )
return _pci->FilterStoreValue( widFake, ps, var, fStored );
return S_OK;
}
SCODE CCI::FilterStoreSecurity(
WORKID widFake,
PSECURITY_DESCRIPTOR pSD,
ULONG cbSD,
BOOL & fStored )
{
fStored = FALSE;
if ( 0 != _pci )
return _pci->FilterStoreSecurity( widFake, pSD, cbSD, fStored );
return S_OK;
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
CRWStore * CCI::ComputeRelevantWords(ULONG cRows,ULONG cRW,
WORKID *pwid,PARTITIONID partid)
{
if ( 0 != _pci )
return _pci->ComputeRelevantWords(cRows,cRW,pwid,partid);
return 0;
} //ComputeRelevantWords
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
CRWStore * CCI::RetrieveRelevantWords(BOOL fAcquire,PARTITIONID partid)
{
if ( 0 != _pci )
return _pci->RetrieveRelevantWords(fAcquire,partid);
return 0;
} //RetrieveRelevantWords
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
unsigned CCI::ReserveUpdate ( WORKID wid )
{
if ( 0 != _pci )
return _pci->ReserveUpdate ( wid );
else
return 0;
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
SCODE CCI::Update ( unsigned iHint,
WORKID wid,
PARTITIONID partid,
USN usn,
VOLUMEID volumeId,
ULONG action )
{
if ( 0 == _pci )
return CI_E_NOT_INITIALIZED;
return _pci->Update ( iHint, wid, partid, usn, volumeId, action );
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::FlushUpdates()
{
if ( 0 != _pci )
_pci->FlushUpdates();
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::MarkOutOfDate()
{
if ( 0 != _pci )
_pci->MarkOutOfDate();
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::MarkUpToDate()
{
if ( 0 != _pci )
_pci->MarkUpToDate();
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
NTSTATUS CCI::CiState(CIF_STATE & state)
{
if ( 0 != _pci )
return _pci->CiState(state);
AssertOnNotFound(STATUS_NOT_FOUND);
return STATUS_NOT_FOUND;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::BackupCiData
//
// Synopsis: Backups Content Index data using the storage provided.
//
// Arguments: [storage] - Destination storage.
// [fFull] - In/Out Set to TRUE if a full save is needed
// On output, set to TRUE if a full save was done because CI
// decided to do a FULL save.
// [xEnumWorkids] - On output, will have the workid enumerator.
// [progressTracker] - Progress feedback and out-of-band abort
// signalling.
//
// History: 3-21-97 srikants Created
//
//----------------------------------------------------------------------------
SCODE CCI::BackupCiData( PStorage & storage,
BOOL & fFull,
XInterface<ICiEnumWorkids> & xEnumWorkids,
PSaveProgressTracker & progressTracker )
{
SCODE sc = CI_E_INVALID_STATE;
if ( 0 != _pci )
{
sc = _pci->BackupCiData( storage, fFull, xEnumWorkids, progressTracker );
if ( !SUCCEEDED(sc) )
{
//
// Cleanup any partially created files.
//
storage.DeleteAllCiFiles();
}
}
return sc;
}
NTSTATUS CCI::IndexSize( ULONG & mbIndex )
{
if ( 0 != _pci )
{
_pci->IndexSize( mbIndex );
return S_OK;
}
AssertOnNotFound(STATUS_NOT_FOUND);
mbIndex = 0;
return STATUS_NOT_FOUND;
}
NTSTATUS CCI::IsLowOnDiskSpace( BOOL & fLow )
{
if ( 0 != _pci )
{
fLow = _pci->IsLowOnDiskSpace();
return S_OK;
}
AssertOnNotFound(STATUS_NOT_FOUND);
fLow = FALSE;
return STATUS_NOT_FOUND;
}
NTSTATUS CCI::VerifyIfLowOnDiskSpace( BOOL & fLow )
{
if ( 0 != _pci )
{
fLow = _pci->VerifyIfLowOnDiskSpace();
return S_OK;
}
AssertOnNotFound(STATUS_NOT_FOUND);
fLow = FALSE;
return STATUS_NOT_FOUND;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::Query, public
//
// Synopsis: Resolves a free-text query.
//
// Effects: Parses a free text query and creates a tree of cursors to
// resolve the query.
//
// Arguments: [pRst] -- Pointer to tree of query restriction(s)
// [pFlags] -- Holds information about the status of the query
// [cPartitions] -- The number of partitions in aPartID.
// [aPartID] -- Array of partitionIDs specifying partitions
// to be searched.
// [cPendingUpdates] -- The number of pending updates that
// should be allowed before using enumeration on
// property queries.
// [cMaxNodes] -- Maximum query expansion.
//
// Requires: All partitions in aPartID are valid.
//
// Returns: A cursor to iterate over the results
//
// Modifies: pFlags may be modified to reflect the status of the query
//
// History: 19-Sep-91 BartoszM Created.
//
//----------------------------------------------------------------------------
CCursor* CCI::Query( CRestriction const * pRst,
ULONG* pFlags,
UINT cPartitions,
PARTITIONID aPartID[],
ULONG cPendingUpdates,
ULONG cMaxNodes )
{
if ( _pci )
return _pci->Query( pRst,
pFlags,
cPartitions,
aPartID,
cPendingUpdates,
cMaxNodes );
return 0;
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
NTSTATUS CCI::Dismount()
{
if ( 0 != _pci )
{
_pci->Dismount();
_xPropMapper.Free();
return S_OK;
}
return STATUS_NOT_FOUND;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::FilterReady, public
//
// Synopsis: Retrieves list of documents to be filtered. Blocks thread if
// resources are not available or if there are no documents to
// be filtered.
//
// Arguments: [docBuffer] -- (in, out) buffer for paths and properties of
// documents to be filtered.
// [cwc] -- (in) count of BYTES in docBuffer
// [cMaxDocs] -- (in) the maximum number of docs that can be
// filtered at once by the filter daemon.
//
// History: 18-Apr-93 AmyA Created.
// 21-Jan-97 SrikantS Changed to BYTE buffers for framework
//
//----------------------------------------------------------------------------
SCODE CCI::FilterReady ( BYTE * docBuffer, ULONG & cb, ULONG cMaxDocs )
{
if ( 0 != _pci )
return( _pci->FilterReady( docBuffer, cb, cMaxDocs ) );
AssertOnNotFound(STATUS_NOT_FOUND);
return STATUS_NOT_FOUND;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::FilterDataReady, public
//
// Synopsis: Adds the contents of entry buffer 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 CCI::FilterDataReady( const BYTE * pEntryBuf, ULONG cb )
{
Win4Assert( _pci );
if ( 0 != _pci )
return( _pci->FilterDataReady( pEntryBuf, cb ) );
AssertOnNotFound(STATUS_NOT_FOUND);
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//
// Member: CCI::FilterMore, public
//
// Synopsis: Commits the current word list and initializes a new one.
//
// Arguments: [aStatus] -- array of STATUS for the resource manager
// [count] -- count for array
//
// History: 03-May-93 AmyA Created.
//
//----------------------------------------------------------------------------
SCODE CCI::FilterMore( const STATUS * aStatus, ULONG count )
{
if ( 0 != _pci )
return( _pci->FilterMore( aStatus, count ) );
AssertOnNotFound(STATUS_NOT_FOUND);
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//
// Member: CCI::FilterDone, public
//
// Synopsis: Commits the current word list.
//
// Arguments: [aStatus] -- array of STATUS for the resource manager
// [count] -- count for array
//
// History: 22-Mar-93 AmyA Created.
//
//----------------------------------------------------------------------------
SCODE CCI::FilterDone( const STATUS * aStatus, ULONG count )
{
if ( 0 != _pci )
return( _pci->FilterDone( aStatus, count ) );
AssertOnNotFound(STATUS_NOT_FOUND);
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//
// Member: CCI::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 CCI::FPSToPROPID( CFullPropSpec const & fps, PROPID & pid )
{
if ( 0 != _pci )
return _pci->FPSToPROPID( fps, pid );
AssertOnNotFound(STATUS_NOT_FOUND);
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//
// Member: CCI::GetFilterProxy
//
// Synopsis: Returns the pointer of the filter proxy for use in
// in-process filtering.
//
// History: 2-13-97 srikants Created
//
//----------------------------------------------------------------------------
CiProxy * CCI::GetFilterProxy()
{
Win4Assert( _pci );
if ( 0 != _pci )
{
return( &(_pci->GetFilterProxy()) );
}
else
{
return 0;
}
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
NTSTATUS CCI::ForceMerge( PARTITIONID partid, CI_MERGE_TYPE mt )
{
if ( _pci )
return _pci->ForceMerge( partid, mt );
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
NTSTATUS CCI::AbortMerge( PARTITIONID partid )
{
if ( _pci )
return _pci->AbortMerge( partid );
return(STATUS_NOT_FOUND);
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::CreatePartition(PARTITIONID partID)
{
if ( _pci )
_pci->CreatePartition ( partID );
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::DestroyPartition(PARTITIONID partID)
{
if ( _pci )
_pci->DestroyPartition ( partID );
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::MergePartitions(PARTITIONID partDest,
PARTITIONID partSrc)
{
if ( _pci )
_pci->MergePartitions(partDest, partSrc);
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
class CBufScanner
{
public:
CBufScanner ( int cb, BYTE UNALIGNED * buf )
{
_buf = buf;
_end = &buf[cb];
_cur = buf;
}
ULONG GetUlong()
{
ULONG x = 0;
if (_end - _cur >= sizeof(ULONG))
{
x = *((ULONG UNALIGNED *)_cur );
_cur += sizeof(ULONG);
}
return(x);
}
private:
BYTE UNALIGNED * _buf;
BYTE UNALIGNED * _end;
BYTE UNALIGNED * _cur;
};
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
void CCI::BackDoor ( int cb, BYTE* buf )
{
ULONG u = 0;
if ( buf == 0 || cb < sizeof(ULONG) )
{
ciDebugOut ((DEB_ITRACE, "BACKDOOR: cb = %d buf = %x\n", cb, buf ));
return;
}
//
// Is CI mounted? If not, don't do anything.
//
if (0 == _pci)
THROW(CException(STATUS_NOT_FOUND));
CBufScanner bufScan ( cb, buf );
CiCommand cmd = (CiCommand) bufScan.GetUlong();
switch (cmd)
{
case CiQuery:
break;
case CiUpdate:
break;
case CiDelete:
break;
case CiPartCreate:
break;
case CiPartDelete:
break;
case CiPartMerge:
break;
case CiPendingUpdates:
*((unsigned *)buf) = _pci->CountPendingUpdates();
break;
case CiInfoLevel:
#if CIDBG == 1
u = bufScan.GetUlong();
ciDebugOut ((DEB_ITRACE, "Info level %lx, new %lx\n", ciInfoLevel, u ));
ciInfoLevel = u; //bufScan.GetUlong();
#endif // CIDBG == 1
break;
case CiDumpIndex:
{
#if (CIDBG == 1)
FILE * pf = fopen( (char *) ULongToPtr( bufScan.GetUlong() ), "w" );
INDEXID iid = (INDEXID)bufScan.GetUlong();
ULONG fSummaryOnly = bufScan.GetUlong();
if ( pf && _pci )
{
_pci->DumpIndexes( pf, iid, fSummaryOnly );
fclose( pf );
}
#endif // CIDBG == 1
}
break;
#if CIDBG == 1
case CiDumpWorkId:
{
_pci->DumpWorkId( buf, cb );
break;
}
#endif
default:
ciDebugOut ((DEB_ERROR, "BACKDOOR: Unknown Command %d\n", cmd ));
break;
}
}
#if CIDBG == 1
ULONG CCI::SetInfoLevel ( ULONG level )
{
ULONG tmp = ciInfoLevel;
ciInfoLevel = level;
return tmp;
}
#endif // CIDBG == 1
//+---------------------------------------------------------------------------
//
// Member: CCI::KeyToId, public
//
// Synopsis: Maps from a key to an id.
//
// Arguments: [pkey] -- pointer to the key to be mapped to ULONG
//
// Returns: key id - a ULONG
//
// History: 03-Nov-93 w-Patg Created.
//
//----------------------------------------------------------------------------
ULONG CCI::KeyToId( CKey const * pkey )
{
if ( 0 != _pci )
{
return _pci->KeyToId( pkey );
}
else
return(kidInvalid);
}
//+---------------------------------------------------------------------------
//
// Member: CCI::IdToKey, public
//
// Synopsis: Maps from an id to a key.
//
// Arguments: [ulKid] -- key id to be mapped to a key
// [rkey] -- reference to the returned key
//
// Returns: void
//
// History: 03-Nov-93 w-Patg Created.
//
//----------------------------------------------------------------------------
void CCI::IdToKey( ULONG ulKid, CKey & rkey )
{
if ( 0 != _pci )
_pci->IdToKey( ulKid, rkey );
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
inline void CCI::SetPartition( PARTITIONID PartId )
{
if ( 0 != _pci )
{
_pci->SetPartition( PartId );
}
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
inline PARTITIONID CCI::GetPartition() const
{
if ( 0 != _pci )
{
return _pci->GetPartition();
}
else
return( partidInvalid );
}
//+---------------------------------------------------------------------------
//
// Member: CCI::Create, public
//
// Synopsis: Sets up a new content index. If there is a partially deleted
// one, it deletes it if there are no outstanding queries.
//
// Returns: [STATUS_SUCCESS] -- a new content index was created
// [CI_CORRUPT_DATABASE] -- CI corrupt, rescan required
// [ERROR_ALREADY_EXISTS] -- catalog already exists
// [ERROR_BUSY] -- one or more queries are outstanding on
// the partially deleted content index
//
// History: 16-Aug-94 DwightKr Created
//
//----------------------------------------------------------------------------
SCODE CCI::Create( ICiCDocStore * pICiCDocStore,
CI_STARTUP_INFO const & startupInfo,
CCiFrameworkParams & params,
XInterface<CIndexNotificationTable> & xIndexNotifTable )
{
CLock lock( _mutex );
if ( (0 != _pci) && (!_pci->IsEmpty()) && (_pci->GetQueryCount() != 0) )
{
#if CIDBG==1
if ( 0 != _pci )
ciDebugOut ((DEB_ERROR, "Can not create a new content index because one already exists\n" ));
if ( !_pci->IsEmpty() )
ciDebugOut ((DEB_ERROR, "Can not create a new content index because the current one is not empty\n" ));
if ( _pci->GetQueryCount() != 0 )
ciDebugOut ((DEB_ERROR, "Can not create a new content index because query are outstanding on the current index\n" ));
#endif // CIDBG==1
return ERROR_ALREADY_EXISTS;
}
CContentIndex * pciNew = 0;
CContentIndex * pciOld = _pci;
NTSTATUS status = STATUS_SUCCESS;
CTransaction xact;
TRY
{
pciNew = new CContentIndex( _storage,
params,
pICiCDocStore,
startupInfo,
_xPropMapper.GetPointer(),
xact,
xIndexNotifTable );
}
CATCH( CException, e )
{
status = e.GetErrorCode();
ciDebugOut(( DEB_ERROR, "No content index found\n" ));
if ( STATUS_NO_MEMORY == e.GetErrorCode() ||
STATUS_INSUFFICIENT_RESOURCES == e.GetErrorCode() )
{
ciDebugOut(( DEB_ERROR, "**** CI Low On Resources **** \n" ));
}
else if ( CI_CORRUPT_DATABASE == e.GetErrorCode() ||
CI_CORRUPT_CATALOG == e.GetErrorCode() ||
STATUS_NOT_FOUND == e.GetErrorCode() )
{
AssertOnNotFound(e.GetErrorCode());
ciDebugOut(( DEB_ERROR,
"**** INCONSISTENCY IN CI. NEED COMPLETE RESCAN ****\n"));
status = CI_CORRUPT_DATABASE;
}
else
{
ciDebugOut(( DEB_ERROR,
"**** Error Code 0x%X ****\n", e.GetErrorCode() ));
}
}
END_CATCH
if ( STATUS_SUCCESS == status )
{
xact.Commit();
//
// If there is anyone using the old index, we assume they will be
// done with it in 15 seconds. If they are not, they get an error
// telling them the CI disappeared from under them.
_pci = pciNew;
if ( pciOld )
{
Sleep(15 * 1000);
delete pciOld;
pciOld = 0;
}
}
return status;
}
//+---------------------------------------------------------------------------
//
// Member: CCI::Empty, public
//
// Synopsis: Deletes all persistent structures from the content index.
//
// History: 16-Aug-94 DwightKr Created
//
//----------------------------------------------------------------------------
void CCI::Empty()
{
CLock lock( _mutex );
if ( 0 != _pci )
{
_pci->Empty();
Win4Assert ( _pci->IsEmpty() );
if ( _pci->GetQueryCount() == 0 )
{
delete _pci;
_pci = 0;
}
}
else
{
ciDebugOut(( DEB_ITRACE, "Disabling CI without CI mounted\n" ));
_storage.DeleteAllCiFiles();
}
}
//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
NTSTATUS CCI::MarkCorruptIndex()
{
if ( _pci )
return _pci->MarkCorruptIndex();
return(STATUS_NOT_FOUND);
}
#if 0
//+---------------------------------------------------------------------------
//
// Member: CCI::FilterPidRemapper, public
//
// Synopsis: Maps a pidMapper into a array of real pids
//
// Arguments: [pbBuffer] - serialized pidMap buffer
// [cbBuffer] - size of the serialized buffer in bytes
// [aPids] - resulting array of real pids
// [cPids] - number of valid entries in the pidRemapArray
//
// Returns: STATUS_SUCCESS
// STATUS_BUFFER_TOO_SMALL - pidRemapArray to small
//
// History: 01-Mar-95 DwightKr Created
//
// Note: The pbBuffer must be used before the aPids is written
// since they may point to the same address.
//----------------------------------------------------------------------------
NTSTATUS CCI::FilterPidRemapper( BYTE * pbBuffer,
unsigned cbBuffer,
PROPID * aPids,
unsigned & cPids )
{
if ( 0 == _pci )
return CI_E_SHUTDOWN;
CMemDeSerStream pidMapDeSerStream( pbBuffer, cbBuffer );
CPidMapper pidMap( pidMapDeSerStream );
//
// We're finished with pbBuffer, it is therefore safe to use aPids
//
//
// If the output buffer is too small, then return an appropriate error
//
if ( cPids < pidMap.Count() )
{
cPids = pidMap.Count();
return STATUS_BUFFER_TOO_SMALL;
}
CSimplePidRemapper pidRemap;
_pci->PidMapToPidRemap( pidMap, pidRemap );
Win4Assert( pidMap.Count() == pidRemap.GetCount() );
cPids = pidMap.Count();
RtlCopyMemory( aPids, pidRemap.GetPropidArray(), cPids * sizeof PROPID );
return STATUS_SUCCESS;
}
#endif