8543 lines
264 KiB
C++
8543 lines
264 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 2000.
|
|
//
|
|
// File: CICAT.CXX
|
|
//
|
|
// Contents: Content index temporary catalog
|
|
//
|
|
// Classes:
|
|
//
|
|
// History: 09-Mar-1992 BartoszM Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <lm.h>
|
|
|
|
#include <fsciexps.hxx>
|
|
#include <ciregkey.hxx>
|
|
#include <regacc.hxx>
|
|
#include <eventlog.hxx>
|
|
#include <doclist.hxx>
|
|
#include <fdaemon.hxx>
|
|
#include <update.hxx>
|
|
#include <pstore.hxx>
|
|
#include <mmstrm.hxx>
|
|
#include <pidremap.hxx>
|
|
#include <imprsnat.hxx>
|
|
#include <nntpprop.hxx>
|
|
#include <ciguid.hxx>
|
|
|
|
#include <docstore.hxx>
|
|
|
|
#include <cievtmsg.h>
|
|
#include <cifailte.hxx>
|
|
#include <pathpars.hxx>
|
|
#include <regscp.hxx>
|
|
#include <cimbmgr.hxx>
|
|
#include <regprop.hxx>
|
|
#include <cifrmcom.hxx>
|
|
#include <glbconst.hxx>
|
|
#include <dmnproxy.hxx>
|
|
#include <propspec.hxx>
|
|
#include <lcase.hxx>
|
|
|
|
#include "propiter.hxx"
|
|
#include "propobj.hxx"
|
|
#include "cicat.hxx"
|
|
#include "cinulcat.hxx"
|
|
#include "catreg.hxx"
|
|
|
|
#include <timlimit.hxx>
|
|
#include <fa.hxx>
|
|
#include <dynmpr.hxx>
|
|
|
|
static const GUID guidCharacterization = PSGUID_CHARACTERIZATION;
|
|
static const unsigned propidCharacterization = 2;
|
|
|
|
static const GUID guidDocSummary = DocPropSetGuid;
|
|
static const PROPID propidTitle = PIDSI_TITLE;
|
|
|
|
//
|
|
// Given a registry string like \Registry\Machine\System\CurrentControlSet\...
|
|
// you skip the first 18 characters if using w/ HKEY_LOCAL_MACHINE.
|
|
//
|
|
|
|
unsigned const SKIP_TO_LM = 18;
|
|
|
|
// -------------------------------------------------------------------------
|
|
// DO NOT VIOLATE THE FOLLOWING LOCK HIERARCHY
|
|
//
|
|
// 1. DocStore Lock
|
|
// 2. CiCat Admin Lock
|
|
// 3. CiCat Lock
|
|
// 4. Resman Lock
|
|
// 5. ScopeTable Lock
|
|
// 6. NotifyMgr Lock
|
|
// 7. ScanMgr Lock
|
|
// 8. Propstore write lock
|
|
// 9. Propstore lock record
|
|
//
|
|
// It is okay for a thread to acquire lock at level X and then acquire a lock
|
|
// at a level >= X but not locks < X. This will avoid deadlock situations.
|
|
//
|
|
// SrikantS - April 25, 1996
|
|
// SrikantS _ Dec 31, 1996 - Added DocStore as the highest level lock
|
|
//
|
|
// -------------------------------------------------------------------------
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CiCat, public
|
|
//
|
|
// Synopsis: Creates a new 'content index catalog'
|
|
//
|
|
// Arguments: [docStore] - Doc store
|
|
// [workMan] - Work queue manager
|
|
// [wcsCatPath] - root path of catalog
|
|
// [fVersionChange] - Set to true if there is a format version
|
|
// change.
|
|
// [pwcName] - name of the catalog from the registry
|
|
//
|
|
// History: 10-Mar-92 BartoszM Created
|
|
// 14-mar-92 KyleP Added Content index object.
|
|
// 03-Mar-98 KitmanH Added code to deal with read-only catalogs
|
|
// 02-Apr-98 KitmanH Start the CiCat in r/o mode if
|
|
// fOpenForReadOnly is TRUE
|
|
// 01-Nov-98 KLam Pass DiskSpaceToLeave to CiStorage
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CiCat::CiCat ( CClientDocStore & docStore,
|
|
CWorkManager & workMan,
|
|
WCHAR const * wcsCatPath,
|
|
BOOL &fVersionChange,
|
|
BOOL fOpenForReadOnly,
|
|
CDrvNotifArray & DrvNotifArray,
|
|
WCHAR const * pwcName,
|
|
BOOL fLeaveCorruptCatalog )
|
|
: _regParams( pwcName ),
|
|
_ulSignature( LONGSIG( 'c', 'c', 'a', 't' ) ),
|
|
_fIsReadOnly( _regParams.IsReadOnly() ),
|
|
_statusMonitor(_wcsCatDir),
|
|
_pStorage(0),
|
|
_state(eStarting),
|
|
_docStore(docStore),
|
|
_PartId(1),
|
|
_fInitialized(FALSE),
|
|
_workMan( workMan ),
|
|
#pragma warning( disable : 4355 ) // this used in base initialization
|
|
_strings( _propstoremgr, *this ),
|
|
_fileIdMap( _propstoremgr ),
|
|
_scanMgr(*this),
|
|
_usnMgr(*this),
|
|
_notify(*this, _scanMgr),
|
|
_scopeTable(*this, _notify, _scanMgr, _usnMgr),
|
|
#pragma warning( default : 4355 )
|
|
_fRecovering( FALSE ),
|
|
_fRecoveryCompleted( FALSE ),
|
|
_propstoremgr( _regParams.GetMinDiskSpaceToLeave() ),
|
|
_fAutoAlias( TRUE ),
|
|
_fIndexW3Roots( FALSE ),
|
|
_fIndexNNTPRoots( FALSE ),
|
|
_fIndexIMAPRoots( FALSE ),
|
|
_W3SvcInstance( 1 ),
|
|
_NNTPSvcInstance( 1 ),
|
|
_IMAPSvcInstance( 1 ),
|
|
_fIsIISAdminAlive( FALSE ),
|
|
_impersonationTokenCache( pwcName ),
|
|
_evtRescanTC( 0 ),
|
|
_cIISSynchThreads( 0 ),
|
|
_cUsnVolumes(0),
|
|
_DrvNotifArray(DrvNotifArray)
|
|
{
|
|
Win4Assert( 0 != pwcName );
|
|
|
|
ciDebugOut(( DEB_WARN, "CiCat::CiCat: %ws\n", pwcName ));
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
//
|
|
// Configure the property store
|
|
//
|
|
|
|
_propstoremgr.SetBackupSize(_regParams.GetPrimaryStoreBackupSize(), PRIMARY_STORE);
|
|
_propstoremgr.SetMappedCacheSize(_regParams.GetPrimaryStoreMappedCache(), PRIMARY_STORE);
|
|
_propstoremgr.SetBackupSize(_regParams.GetSecondaryStoreBackupSize(), SECONDARY_STORE);
|
|
_propstoremgr.SetMappedCacheSize(_regParams.GetSecondaryStoreMappedCache(), SECONDARY_STORE);
|
|
|
|
fVersionChange = FALSE;
|
|
RtlZeroMemory( &_ftLastCLFlush, sizeof(_ftLastCLFlush) );
|
|
|
|
//
|
|
// Get path to hives. Needed to avoid indexing them (oplock problems).
|
|
//
|
|
|
|
//
|
|
// Use _wcsCatDir as scratch space.
|
|
//
|
|
|
|
WCHAR const wcsConfigDir[] = L"\\config";
|
|
|
|
if ( 0 != GetSystemDirectory( _wcsCatDir,
|
|
(sizeof(_wcsCatDir) - sizeof(wcsConfigDir))/sizeof(WCHAR) ) )
|
|
{
|
|
wcscat( _wcsCatDir, wcsConfigDir );
|
|
}
|
|
|
|
//
|
|
// Set up catalog paths
|
|
//
|
|
|
|
CLowcaseBuf PathBuf( wcsCatPath );
|
|
|
|
if ( PathBuf.Length() >= MAX_CAT_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Path for catalog (%ws) is too long\n",
|
|
wcsCatPath ));
|
|
CCiStatusMonitor::ReportPathTooLong( wcsCatPath );
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
}
|
|
|
|
_xwcsDriveName.SetBuf( PathBuf.Get(), wcslen( PathBuf.Get() ) + 1 );
|
|
wcscpy ( _wcsCatDir, PathBuf.Get() );
|
|
wcscat ( _wcsCatDir, CAT_DIR );
|
|
|
|
_CatDir.Init( _wcsCatDir, wcslen( _wcsCatDir ) );
|
|
|
|
SetName( pwcName );
|
|
BuildRegistryScopesKey( _xScopesKey, pwcName );
|
|
|
|
// Note: indexsrv uses the full path with catalog.wci while query
|
|
// uses just the cat path.
|
|
|
|
CSharedNameGen nameGen( wcsCatPath );
|
|
_evtRescanTC.Set( new CEventSem( nameGen.GetRescanTCEventName() ) );
|
|
_evtRescanTC->Reset();
|
|
|
|
_fAutoAlias = _regParams.IsAutoAlias();
|
|
_fIndexW3Roots = _regParams.IsIndexingW3Roots();
|
|
_fIndexNNTPRoots = _regParams.IsIndexingNNTPRoots();
|
|
_fIndexIMAPRoots = _regParams.IsIndexingIMAPRoots();
|
|
_W3SvcInstance = _regParams.GetW3SvcInstance();
|
|
_NNTPSvcInstance = _regParams.GetNNTPSvcInstance();
|
|
_IMAPSvcInstance = _regParams.GetIMAPSvcInstance();
|
|
|
|
// CImpersonateSystem::_fRunningAsSystem will already be set as TRUE
|
|
// if running in CiSvc
|
|
|
|
if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots )
|
|
{
|
|
CImpersonateSystem::SetRunningAsSystem();
|
|
|
|
BOOL fInstalled;
|
|
_fIsIISAdminAlive = CMetaDataMgr::IsIISAdminUp( fInstalled );
|
|
|
|
if ( !_fIsIISAdminAlive )
|
|
{
|
|
// if IIS isn't installed, don't bother at all
|
|
|
|
if ( !fInstalled )
|
|
{
|
|
_fIndexW3Roots = FALSE;
|
|
_fIndexNNTPRoots = FALSE;
|
|
_fIndexIMAPRoots = FALSE;
|
|
ciDebugOut(( DEB_WARN, "IIS isn't installed!!!\n" ));
|
|
}
|
|
|
|
EvtLogIISAdminNotAvailable();
|
|
}
|
|
}
|
|
|
|
_impersonationTokenCache.Initialize( CI_ACTIVEX_NAME,
|
|
_fIndexW3Roots,
|
|
_fIndexNNTPRoots,
|
|
_fIndexIMAPRoots,
|
|
_W3SvcInstance,
|
|
_NNTPSvcInstance,
|
|
_IMAPSvcInstance );
|
|
|
|
#if CIDBG == 1
|
|
if ( _fIndexW3Roots )
|
|
ciDebugOut(( DEB_ITRACE, "Indexing W3 roots in %d\n", _W3SvcInstance ));
|
|
if ( _fIndexNNTPRoots )
|
|
ciDebugOut(( DEB_ITRACE, "Indexing NNTP roots in %d\n", _NNTPSvcInstance ));
|
|
if ( _fIndexIMAPRoots )
|
|
ciDebugOut(( DEB_ITRACE, "Indexing IMAP roots in %d\n", _IMAPSvcInstance ));
|
|
|
|
ciDebugOut(( DEB_ITRACE, "_fIsIISAdminAlive: %d\n", _fIsIISAdminAlive ));
|
|
#endif // CIDBG == 1
|
|
|
|
// obtain an ICiCAdviseStatus interface pointer to use
|
|
|
|
ICiCAdviseStatus *pAdviseStatus = 0;
|
|
|
|
SCODE sc = _docStore.QueryInterface( IID_ICiCAdviseStatus,
|
|
(void **) &pAdviseStatus);
|
|
if ( S_OK != sc )
|
|
{
|
|
Win4Assert( 0 == pAdviseStatus );
|
|
THROW( CException(sc) );
|
|
}
|
|
|
|
_xAdviseStatus.Set(pAdviseStatus);
|
|
|
|
XPtr<PStorage> xStorage;
|
|
|
|
_pStorage = new CiStorage ( _wcsCatDir,
|
|
_xAdviseStatus.GetReference(),
|
|
_regParams.GetMinDiskSpaceToLeave(),
|
|
FSCI_VERSION_STAMP,
|
|
_fIsReadOnly );
|
|
|
|
xStorage.Set( _pStorage );
|
|
|
|
// in case the catalog allows only read-only access, but it was not marked
|
|
// as read-only in the registry, refresh _fIsReadOnly value and setreadonly()
|
|
// for _scanMgr, if _pStorage is found as IsReadOnly() after its construction
|
|
// Also if we are in "net paused" state, we're restarting the CiCat in r/o
|
|
// mode
|
|
|
|
if ( _pStorage->IsReadOnly() || fOpenForReadOnly )
|
|
{
|
|
_fIsReadOnly = TRUE;
|
|
_scanMgr.SetReadOnly();
|
|
_pStorage->SetReadOnly(); //refresh it if we're in "net pause"
|
|
}
|
|
|
|
//
|
|
// The scope table is initialized to check the corruption status
|
|
// and format version.
|
|
//
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
TRY
|
|
{
|
|
_scopeTable.FastInit();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
status = e.GetErrorCode();
|
|
ciDebugOut(( DEB_ERROR,
|
|
"Error 0x%x while initializing scopetable\n",
|
|
status ));
|
|
}
|
|
END_CATCH
|
|
|
|
if ( status != STATUS_SUCCESS )
|
|
{
|
|
if ( IsCiCorruptStatus(status) || status == CI_INCORRECT_VERSION )
|
|
{
|
|
// if catalog is corrupted and the catalog is readonly, We cannot recover
|
|
if ( IsReadOnly() )
|
|
{
|
|
Win4Assert( !"Catalog is read-only, cannot be recovered!" );
|
|
_statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed, status );
|
|
THROW( CException( status ) );
|
|
}
|
|
|
|
#if CIDBG==1
|
|
//if ( IsCiCorruptStatus(status) )
|
|
// Win4Assert( !"FSCI(Catalog) Data Corruption" );
|
|
#endif
|
|
if ( status == CI_INCORRECT_VERSION )
|
|
fVersionChange = TRUE;
|
|
|
|
// If instructed to leave corrupt catalogs, throw now.
|
|
// Don't leave around catalogs that need to change due to a
|
|
// version change. Note that some corruptions may just look
|
|
// like a version change, but that's the way it goes.
|
|
|
|
if ( fLeaveCorruptCatalog && !fVersionChange )
|
|
{
|
|
_statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed, status );
|
|
Win4Assert( !"leaving corrupt catalog" );
|
|
THROW( CException( status ) );
|
|
}
|
|
|
|
//
|
|
// Log an event that we are doing automatic recovery.
|
|
//
|
|
_statusMonitor.LogEvent( CCiStatusMonitor::eCiRemoved );
|
|
_pStorage->DeleteAllFsCiFiles();
|
|
|
|
_statusMonitor.Reset();
|
|
_scopeTable.FastInit();
|
|
}
|
|
else
|
|
{
|
|
if ( status == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) )
|
|
_statusMonitor.LogEvent( CCiStatusMonitor::eInitFailed,
|
|
status );
|
|
|
|
THROW( CException( status ) );
|
|
}
|
|
}
|
|
|
|
xStorage.Acquire();
|
|
|
|
//
|
|
// Start all the threads up, after we can THROW.
|
|
//
|
|
|
|
_notify.Resume();
|
|
_usnMgr.Resume();
|
|
_scanMgr.Resume();
|
|
} //CiCat
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EvtLogIISAdminNotAvailable(), private
|
|
//
|
|
// Synopsis: Logs an event that iisadmin was needed but not available
|
|
//
|
|
// History: 2-19-97 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::EvtLogIISAdminNotAvailable()
|
|
{
|
|
TRY
|
|
{
|
|
CEventLog eventLog( NULL, wcsCiEventSource );
|
|
|
|
CEventItem item( EVENTLOG_WARNING_TYPE,
|
|
CI_SERVICE_CATEGORY,
|
|
MSG_CI_IISADMIN_NOT_AVAILABLE,
|
|
0 );
|
|
|
|
eventLog.ReportEvent( item );
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"Exception 0x%X while writing to event log\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //EvtLogIISAdminNotAvailable
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::PathToFileId, private
|
|
//
|
|
// Synopsis: Looks up a fileid based on a path
|
|
//
|
|
// Arguments: [funnyPath] -- Full funny path of the file.
|
|
//
|
|
// Returns: The fileid of the file or fileIdInvalid if not found.
|
|
//
|
|
// History: 3-3-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
FILEID CiCat::PathToFileId( const CFunnyPath & funnyPath )
|
|
{
|
|
FILEID fileId = fileIdInvalid;
|
|
|
|
BOOL fAppendBackSlash = FALSE;
|
|
|
|
// For volume \\?\D:, NtQueryInformationFile with the following
|
|
// error: 0xC0000010L - STATUS_INVALID_DEVICE_REQUEST. Need to append a \,
|
|
// to make it work. We need to append the '\'only in case of volume path.
|
|
|
|
if ( 2 == funnyPath.GetActualLength() && !funnyPath.IsRemote() )
|
|
{
|
|
Win4Assert( L':' == (funnyPath.GetActualPath())[1] );
|
|
((CFunnyPath&)funnyPath).AppendBackSlash(); // override const
|
|
fAppendBackSlash = TRUE;
|
|
}
|
|
|
|
HANDLE h;
|
|
NTSTATUS status = CiNtOpenNoThrow( h,
|
|
funnyPath.GetPath(),
|
|
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
0 );
|
|
|
|
if ( fAppendBackSlash )
|
|
{
|
|
((CFunnyPath&)funnyPath).RemoveBackSlash(); // override const
|
|
}
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
{
|
|
SHandle xFile( h );
|
|
|
|
FILE_INTERNAL_INFORMATION fii;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
status = NtQueryInformationFile( h,
|
|
&IoStatus,
|
|
&fii,
|
|
sizeof fii,
|
|
FileInternalInformation );
|
|
if ( NT_SUCCESS( status ) )
|
|
status = IoStatus.Status;
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
{
|
|
fileId = fii.IndexNumber.QuadPart;
|
|
Win4Assert( fileIdInvalid != fileId );
|
|
}
|
|
}
|
|
|
|
if ( NT_ERROR( status ) )
|
|
{
|
|
// ignore and return fileIdInvalid if the file doesn't exist.
|
|
|
|
if ( ! ( IsSharingViolation( status ) ||
|
|
STATUS_ACCESS_DENIED == status ||
|
|
STATUS_DELETE_PENDING == status ||
|
|
STATUS_OBJECT_PATH_NOT_FOUND == status ||
|
|
STATUS_OBJECT_NAME_NOT_FOUND == status ||
|
|
STATUS_OBJECT_NAME_INVALID == status ||
|
|
STATUS_NO_MEDIA_IN_DEVICE == status ||
|
|
STATUS_NONEXISTENT_SECTOR == status ||
|
|
STATUS_IO_REPARSE_TAG_NOT_HANDLED == status ) )
|
|
{
|
|
#if CIDBG == 1
|
|
if ( STATUS_WRONG_VOLUME != status &&
|
|
STATUS_VOLUME_DISMOUNTED != status &&
|
|
STATUS_INSUFFICIENT_RESOURCES != status &&
|
|
STATUS_UNRECOGNIZED_VOLUME != status )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"error 0x%x, can't get fileid for '%ws'\n",
|
|
status, funnyPath.GetPath() ));
|
|
|
|
char acTemp[ 200 ];
|
|
sprintf( acTemp, "New error %#x from PathToFileId. Call DLee", status );
|
|
Win4AssertEx(__FILE__, __LINE__, acTemp);
|
|
}
|
|
#endif
|
|
|
|
THROW( CException( status ) );
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "(ok) pathtofileid failed %#x\n", status ));
|
|
}
|
|
}
|
|
|
|
return fileId;
|
|
} //PathToFileId
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::LokLookupWid, private
|
|
//
|
|
// Synopsis: Looks up a workid based on a fileid or path
|
|
//
|
|
// Arguments: [lcaseFunnyPath] -- Full path of the file.
|
|
// [fileId] -- The fileid of the file or fileIdInvalid if
|
|
// the fileid should be found based on pwcPath.
|
|
// Returns the fileId if it was looked up.
|
|
//
|
|
// Returns: The workid of the file or widInvalid if not found.
|
|
//
|
|
// History: 3-3-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
WORKID CiCat::LokLookupWid( const CLowerFunnyPath & lcaseFunnyPath, FILEID & fileId )
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
|
|
if ( fileIdInvalid == fileId )
|
|
fileId = PathToFileId( lcaseFunnyPath );
|
|
|
|
if ( fileIdInvalid == fileId )
|
|
return widInvalid;
|
|
|
|
return _fileIdMap.LokFind( fileId, MapPathToVolumeId( lcaseFunnyPath.GetActualPath() ) );
|
|
} //LokLookupWid
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::PropertyToPropId
|
|
//
|
|
// Synopsis: Locate pid for property
|
|
//
|
|
// Arguments: [ps] -- Property specification (name)
|
|
// [fCreate] -- TRUE if non-existent mapping should be created
|
|
//
|
|
// Returns: The pid of [ps].
|
|
//
|
|
// History: 09 Jan 1996 AlanW Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
PROPID CiCat::PropertyToPropId( CFullPropSpec const & ps,
|
|
BOOL fCreate )
|
|
{
|
|
FULLPROPSPEC const * fps = ps.CastToStruct();
|
|
|
|
if ( IsStarted() )
|
|
return _docStore._PropertyToPropid( fps, fCreate );
|
|
|
|
return _propMapper.PropertyToPropId( ps, fCreate);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::GetStorage, public
|
|
//
|
|
// Returns: Storage object for catalog
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
PStorage& CiCat::GetStorage ()
|
|
{
|
|
return *_pStorage;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::GetDriveName, public
|
|
//
|
|
// Returns: Drive name
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
WCHAR * CiCat::GetDriveName()
|
|
{
|
|
return( _xwcsDriveName.Get() );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::LokExists, private
|
|
//
|
|
// Returns: TRUE if a catalog directory exists.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::LokExists()
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
|
|
if ( IsStarted() )
|
|
return TRUE;
|
|
|
|
DWORD dwAttr = GetFileAttributesW( _wcsCatDir );
|
|
if ( dwAttr == 0xFFFFFFFF )
|
|
{
|
|
ciDebugOut((DEB_ITRACE, "Directory %ws does not exist\n", _wcsCatDir));
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::LokCreate, private
|
|
//
|
|
// Synopsis: Creates content index.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::LokCreate()
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
CreateDirectory ( _wcsCatDir, 0 );
|
|
|
|
ciDebugOut ((DEB_ITRACE, "Catalog Directory %ws created\n", _wcsCatDir ));
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DoRecovery
|
|
//
|
|
// Synopsis: Does the "long" initialization process. If any recovery
|
|
// needs to be done, it will be done here. This is done in
|
|
// the scan threads context and will not allow any other
|
|
// writer to come in.
|
|
//
|
|
// History: 3-06-96 srikants Created
|
|
// 9-Nov-98 KLam Throws CI_E_CONFIG_DISK_FULL if there is no disk space
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::DoRecovery()
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Doing long initialization step\n" ));
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TRY
|
|
{
|
|
if ( !IsStarted() || _statusMonitor.IsCorrupt() )
|
|
{
|
|
// Win4Assert( !"Corrupt catalog" );
|
|
_pStorage->ReportCorruptComponent( L"CiCatalog1" );
|
|
|
|
THROW( CException( CI_CORRUPT_CATALOG ) );
|
|
}
|
|
|
|
//
|
|
// Check for disk full situation and process it accordingly.
|
|
//
|
|
BOOL fLow = _docStore.VerifyIfLowOnDiskSpace();
|
|
|
|
if ( fLow )
|
|
{
|
|
THROW( CException( CI_E_CONFIG_DISK_FULL ) );
|
|
}
|
|
else if ( IsLowOnDisk() )
|
|
{
|
|
//
|
|
// A disk-full situation existed before. Clear it up.
|
|
//
|
|
NoLokClearDiskFull();
|
|
}
|
|
|
|
if ( _propstoremgr.IsDirty() )
|
|
_statusMonitor.ReportPropStoreRecoveryStart();
|
|
|
|
ULONG cInconsistencies;
|
|
_propstoremgr.LongInit( _fRecovering, cInconsistencies, UpdateDuringRecovery, this );
|
|
|
|
if ( _fRecovering )
|
|
{
|
|
if (cInconsistencies)
|
|
{
|
|
_statusMonitor.ReportPropStoreRecoveryError( cInconsistencies );
|
|
THROW( CException( CI_CORRUPT_CATALOG ) );
|
|
}
|
|
else
|
|
_statusMonitor.ReportPropStoreRecoveryEnd();
|
|
}
|
|
|
|
//
|
|
// Add properties to primary store. Will only happen on first call.
|
|
//
|
|
ULONG_PTR ulToken = _propstoremgr.BeginTransaction();
|
|
|
|
_propstoremgr.Setup( pidLastSeenTime, VT_FILETIME, sizeof (FILETIME), ulToken, FALSE, PRIMARY_STORE );
|
|
_propstoremgr.Setup( pidParentWorkId, VT_UI4, sizeof( WORKID ), ulToken, FALSE, PRIMARY_STORE );
|
|
_propstoremgr.Setup( pidAttrib, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE );
|
|
|
|
//
|
|
// Usn/Ntfs 5.0 properties
|
|
//
|
|
|
|
_propstoremgr.Setup( pidFileIndex, VT_UI8, sizeof( FILEID ), ulToken, FALSE, PRIMARY_STORE );
|
|
_propstoremgr.Setup( pidVolumeId, VT_UI4, sizeof( VOLUMEID ), ulToken, FALSE, PRIMARY_STORE );
|
|
|
|
//
|
|
// User properties destined to the primary store
|
|
//
|
|
|
|
RecoverUserProperties( ulToken, PRIMARY_STORE );
|
|
|
|
_propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath );
|
|
|
|
//
|
|
// Add properties to secondary store. Will only happen on first call.
|
|
//
|
|
ulToken = _propstoremgr.BeginTransaction();
|
|
|
|
//
|
|
// 'Standard' properties.
|
|
//
|
|
|
|
CFullPropSpec psTitle( guidDocSummary, propidTitle );
|
|
PROPID pidTitle = PropertyToPropId( psTitle, TRUE );
|
|
|
|
_propstoremgr.Setup( pidSize, VT_I8, sizeof (LONGLONG), ulToken, FALSE, SECONDARY_STORE );
|
|
_propstoremgr.Setup( pidWriteTime, VT_FILETIME, sizeof (LONGLONG), ulToken, FALSE, SECONDARY_STORE );
|
|
_propstoremgr.Setup( pidTitle, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
//
|
|
// 'Characterization'
|
|
//
|
|
|
|
CFullPropSpec psCharacterization( guidCharacterization, propidCharacterization );
|
|
PROPID pidCharacterization = PropertyToPropId( psCharacterization, TRUE );
|
|
|
|
BOOL fCanStoreChar = _propstoremgr.CanStore( pidCharacterization );
|
|
|
|
if ( _regParams.GetGenerateCharacterization() )
|
|
{
|
|
if ( ! fCanStoreChar )
|
|
_propstoremgr.Setup( pidCharacterization,
|
|
VT_LPWSTR,
|
|
4,
|
|
ulToken,
|
|
TRUE,
|
|
SECONDARY_STORE );
|
|
}
|
|
else
|
|
{
|
|
// size of 0 means remove the property
|
|
|
|
if ( fCanStoreChar )
|
|
_propstoremgr.Setup( pidCharacterization,
|
|
VT_LPWSTR,
|
|
0,
|
|
ulToken,
|
|
TRUE,
|
|
SECONDARY_STORE );
|
|
}
|
|
|
|
//
|
|
// IMAP properties
|
|
//
|
|
|
|
if ( _fIndexIMAPRoots )
|
|
{
|
|
CFullPropSpec psNewsReceivedDate( guidNNTP, propidNewsReceivedDate );
|
|
|
|
PROPID pidNewsReceivedDate = PropertyToPropId( psNewsReceivedDate, TRUE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsReceivedDate ) )
|
|
_propstoremgr.Setup( pidNewsReceivedDate, VT_FILETIME, 8, ulToken, TRUE, SECONDARY_STORE );
|
|
}
|
|
|
|
//
|
|
// IMAP/NNTP properties
|
|
//
|
|
|
|
if ( _fIndexNNTPRoots || _fIndexIMAPRoots )
|
|
{
|
|
CFullPropSpec psNewsDate( guidNNTP, propidNewsDate );
|
|
CFullPropSpec psNewsArticleid( guidNNTP, propidNewsArticleid );
|
|
|
|
PROPID pidNewsDate = PropertyToPropId( psNewsDate, TRUE );
|
|
PROPID pidNewsArticleid = PropertyToPropId( psNewsArticleid, TRUE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsDate ) )
|
|
_propstoremgr.Setup( pidNewsDate, VT_FILETIME, 8, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsArticleid ) )
|
|
_propstoremgr.Setup( pidNewsArticleid, VT_UI4, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
}
|
|
|
|
//
|
|
// NNTP properties
|
|
//
|
|
|
|
if ( _fIndexNNTPRoots )
|
|
{
|
|
CFullPropSpec psNewsSubject( guidNNTP, propidNewsSubject );
|
|
CFullPropSpec psNewsFrom( guidNNTP, propidNewsFrom );
|
|
CFullPropSpec psNewsGroup( guidNNTP, propidNewsGroup );
|
|
CFullPropSpec psNewsGroups( guidNNTP, propidNewsGroups );
|
|
CFullPropSpec psNewsReferences( guidNNTP, propidNewsReferences );
|
|
CFullPropSpec psNewsMsgid( guidNNTP, propidNewsMsgid );
|
|
|
|
PROPID pidNewsSubject = PropertyToPropId( psNewsSubject, TRUE );
|
|
PROPID pidNewsFrom = PropertyToPropId( psNewsFrom, TRUE );
|
|
PROPID pidNewsGroup = PropertyToPropId( psNewsGroup, TRUE );
|
|
PROPID pidNewsGroups = PropertyToPropId( psNewsGroups, TRUE );
|
|
PROPID pidNewsReferences = PropertyToPropId( psNewsReferences, TRUE );
|
|
PROPID pidNewsMsgid = PropertyToPropId( psNewsMsgid, TRUE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsSubject ) )
|
|
_propstoremgr.Setup( pidNewsSubject, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsFrom ) )
|
|
_propstoremgr.Setup( pidNewsFrom, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsGroup ) )
|
|
_propstoremgr.Setup( pidNewsGroup, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsGroups ) )
|
|
_propstoremgr.Setup( pidNewsGroups, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsReferences ) )
|
|
_propstoremgr.Setup( pidNewsReferences, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
|
|
if ( !_propstoremgr.CanStore( pidNewsMsgid ) )
|
|
_propstoremgr.Setup( pidNewsMsgid, VT_LPWSTR, 4, ulToken, TRUE, SECONDARY_STORE );
|
|
}
|
|
|
|
//
|
|
// User properties destined to the secondary store
|
|
//
|
|
|
|
RecoverUserProperties( ulToken, SECONDARY_STORE );
|
|
_propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath);
|
|
|
|
//
|
|
// The strings/fileidmap version numbers are 1 greater than the index
|
|
// version, since we needed to change the on-disk format of the hash
|
|
// tables but don't want to force a reindex of the catalog. Hash tables
|
|
// are rebuilt automatically from the property store if the version is
|
|
// incorrect.
|
|
//
|
|
|
|
_strings.LongInit( CURRENT_VERSION_STAMP + 1, _fRecovering );
|
|
_fileIdMap.LongInit( CURRENT_VERSION_STAMP + 1, _fRecovering );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Long Initialization procedure complete\n" ));
|
|
|
|
_evtInitialized.Set();
|
|
_fInitialized = TRUE;
|
|
|
|
#ifdef CI_FAILTEST
|
|
NTSTATUS failStatus = CI_CORRUPT_CATALOG;
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
//
|
|
// Set that the initialization failed and CI is not mounted.
|
|
//
|
|
status = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( STATUS_SUCCESS != status )
|
|
{
|
|
HandleError( status );
|
|
|
|
if ( !CiCat::IsDiskLowError(status) )
|
|
{
|
|
//
|
|
// Report any error other than the disk full error.
|
|
//
|
|
CLock lock(_mutex);
|
|
|
|
if ( _statusMonitor.GetStatus() != status )
|
|
{
|
|
_statusMonitor.SetStatus( status );
|
|
_statusMonitor.ReportInitFailure();
|
|
}
|
|
}
|
|
|
|
THROW( CException( status ) );
|
|
}
|
|
|
|
_fRecovering = FALSE;
|
|
} //DoRecovery
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::StartScansAndNotifies
|
|
//
|
|
// Synopsis: Enables scanning and tracking notifications.
|
|
//
|
|
// History: 12-09-96 srikants Created
|
|
//
|
|
// Notes: Must be called only after recovery is complete.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::StartScansAndNotifies()
|
|
{
|
|
Win4Assert( !_fRecovering );
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TRY
|
|
{
|
|
_scopeTable.StartUp( _docStore, GetPartition() );
|
|
|
|
_evtPh2Init.Set();
|
|
|
|
#ifdef CI_FAILTEST
|
|
NTSTATUS failStatus = CI_CORRUPT_CATALOG;
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
//
|
|
// Just to make sure the shadow virtual root registry entries
|
|
// didn't get messed up...
|
|
//
|
|
|
|
AddShadowScopes();
|
|
|
|
if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots )
|
|
{
|
|
//
|
|
// DO NOT OBTAIN LOCK WHILE DOING SYNC WITH IIS
|
|
//
|
|
|
|
// setup tracking even if iisadmin is hosed -- _notify
|
|
// will poll until iisadmin is up.
|
|
|
|
_notify.TrackIISVRoots( _fIndexW3Roots,
|
|
_W3SvcInstance,
|
|
_fIndexNNTPRoots,
|
|
_NNTPSvcInstance,
|
|
_fIndexIMAPRoots,
|
|
_IMAPSvcInstance );
|
|
}
|
|
|
|
// Always synch with IIS. Indexing of IIS may have been on
|
|
// on the previous run of this catalog but not for this run,
|
|
// so we need to remove those vroots
|
|
|
|
SynchWithIIS( TRUE, FALSE );
|
|
|
|
if ( 0 != GetName() )
|
|
{
|
|
SynchWithRegistryScopes( FALSE );
|
|
_notify.TrackScopesInRegistry();
|
|
}
|
|
|
|
_notify.TrackCiRegistry();
|
|
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
//
|
|
// Set that the initialization failed and CI is not mounted.
|
|
//
|
|
status = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( STATUS_SUCCESS != status )
|
|
{
|
|
HandleError( status );
|
|
|
|
if ( !CiCat::IsDiskLowError(status) )
|
|
{
|
|
//
|
|
// Report any error other than the disk full error.
|
|
//
|
|
CLock lock(_mutex);
|
|
|
|
if ( _statusMonitor.GetStatus() != status )
|
|
{
|
|
_statusMonitor.SetStatus( status );
|
|
_statusMonitor.ReportInitFailure();
|
|
}
|
|
}
|
|
|
|
THROW( CException( status ) );
|
|
}
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{
|
|
CLock lock(_mutex);
|
|
if( IsStarting() )
|
|
_state = eStarted;
|
|
}
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
} //StartScansAndNotifies
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CCiCat::SetRecoveryCompleted
|
|
//
|
|
// Synopsis: Marks that recovery is complete on persistent data
|
|
// structures. It informs the docstore about the completed
|
|
// recovery which can then enable filtering.
|
|
//
|
|
// History: 12-09-96 srikants Created
|
|
//
|
|
// Notes: MUST BE CALLED ONLY BY A WORKER THREAD.
|
|
// Should NOT throw.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::SetRecoveryCompleted()
|
|
{
|
|
Win4Assert( !CImpersonateSystem::IsImpersonated() );
|
|
Win4Assert( !_fRecovering );
|
|
|
|
TRY
|
|
{
|
|
_docStore._SetCiCatRecovered();
|
|
_fRecoveryCompleted = TRUE;
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "SetRecoveryCompleted error (0x%X)\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //SetRecoveryCompleted
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EnableUpdateNotifies
|
|
//
|
|
// Synopsis: Called by the doc store to start scanning and filtering
|
|
// when CI is ready to receive notification changes.
|
|
//
|
|
// History: 12-09-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::EnableUpdateNotifies()
|
|
{
|
|
Win4Assert( !_fRecovering );
|
|
Win4Assert( _fInitialized );
|
|
|
|
_scanMgr.StartScansAndNotifies();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::LokInit, private
|
|
//
|
|
// Synopsis: Opens content index. Index corruption failures during
|
|
// LokInit do not require a restart of the process to blow
|
|
// away the corrupt index
|
|
//
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::LokInit()
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
|
|
ciDebugOut ((DEB_ITRACE, "CiCat::LokInit.. Catalog Directory is %ws\n", _wcsCatDir ));
|
|
|
|
if ( 0 == _xCiManager.GetPointer() )
|
|
THROW( CException( CI_E_NOT_INITIALIZED ) );
|
|
|
|
#ifdef CI_FAILTEST
|
|
NTSTATUS failStatus = CI_CORRUPT_CATALOG;
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
TRY
|
|
{
|
|
ciDebugOut ((DEB_ITRACE, "Opening Catalog %ws\n", _wcsCatDir ));
|
|
|
|
if ( !_scopeTable.IsInit() )
|
|
_scopeTable.FastInit();
|
|
|
|
// Win4Assert( !_scopeTable.IsCiDataCorrupt() ); // Commented out because it's hit by version upgrade.
|
|
|
|
if ( _scopeTable.IsFsCiDataCorrupt() )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Persistent corruption detected in ci %ws\n",
|
|
_wcsCatDir ));
|
|
// Win4Assert( !"Corrupt scope table" ); // Commented out because it's hit by version upgrade.
|
|
_pStorage->ReportCorruptComponent( L"CiCatalog2" );
|
|
|
|
THROW( CException( CI_CORRUPT_CATALOG ) );
|
|
}
|
|
|
|
_propstoremgr.FastInit( _pStorage );
|
|
|
|
//
|
|
// If we went down dirty during an initial scan, blow away the
|
|
// catalog and start again.
|
|
//
|
|
|
|
BOOL fBackedUpMode = _propstoremgr.IsBackedUpMode();
|
|
ciDebugOut(( DEB_ITRACE, "startup: is backed up mode: %d\n", fBackedUpMode ));
|
|
|
|
if ( !fBackedUpMode )
|
|
{
|
|
_pStorage->ReportCorruptComponent( L"Crash during scan" );
|
|
THROW( CException( CI_CORRUPT_CATALOG ) );
|
|
}
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
PRcovStorageObj * pObj = _pStorage->QueryPidLookupTable( 0 );
|
|
|
|
// Init takes ownership of the object regardless of whether it
|
|
// succeeds.
|
|
|
|
if ( !_PidTable.Init( pObj ) )
|
|
{
|
|
ciDebugOut ((DEB_ERROR, "Failed init of PidTable\n"));
|
|
// Win4Assert( !"Corrupt pid table" );
|
|
_pStorage->ReportCorruptComponent( L"CiCatalog3" );
|
|
|
|
THROW (CException(CI_CORRUPT_CATALOG));
|
|
}
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
if ( !_SecStore.Init( _pStorage ) )
|
|
{
|
|
ciDebugOut ((DEB_ERROR, "Failed init of SecStore\n"));
|
|
// Win4Assert( !"Corrupt security store" );
|
|
_pStorage->ReportCorruptComponent( L"CiCatalog4" );
|
|
|
|
THROW (CException(CI_CORRUPT_CATALOG));
|
|
}
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
//
|
|
// On initial creation, this will happen.
|
|
// A transaction can only operate on one store at a time,
|
|
// so separate Setup calls into two transactions.
|
|
//
|
|
|
|
ULONG_PTR ulToken = _propstoremgr.BeginTransaction();
|
|
|
|
_propstoremgr.Setup( pidSecurity, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE );
|
|
//
|
|
// Create a pointer to secondary store top-level wid, which will be the
|
|
// counter part of the top-level in the primary
|
|
//
|
|
_propstoremgr.Setup( pidSecondaryStorage, VT_UI4, sizeof (ULONG), ulToken, FALSE, PRIMARY_STORE );
|
|
|
|
_propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath );
|
|
|
|
ulToken = _propstoremgr.BeginTransaction();
|
|
|
|
_propstoremgr.Setup( pidPath, VT_LPWSTR, MAX_PATH / 3, ulToken, FALSE, SECONDARY_STORE );
|
|
_propstoremgr.Setup( pidVirtualPath, VT_UI4, 4, ulToken, FALSE, SECONDARY_STORE );
|
|
|
|
_propstoremgr.EndTransaction( ulToken, TRUE, pidSecurity, pidVirtualPath );
|
|
|
|
//
|
|
// The strings/fileidmap version numbers are 1 greater than the index
|
|
// version, since we needed to change the on-disk format of the hash
|
|
// tables but don't want to force a reindex of the catalog. Hash tables
|
|
// are rebuilt automatically from the property store if the version is
|
|
// incorrect.
|
|
//
|
|
|
|
_strings.FastInit( _pStorage, CURRENT_VERSION_STAMP + 1 );
|
|
_fileIdMap.FastInit( _pStorage, CURRENT_VERSION_STAMP + 1 );
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
ciDebugOut ((DEB_ITRACE, "Opening Content Index %ws\n", _wcsCatDir ));
|
|
|
|
#ifdef CI_FAILTEST
|
|
ciFAILTEST( failStatus );
|
|
#endif // CI_FAILTEST
|
|
|
|
_state = eQueryOnly;
|
|
|
|
// make fixups available so we can start to process queries asap
|
|
|
|
SetupScopeFixups();
|
|
|
|
_statusMonitor.ReportCIStarted();
|
|
|
|
if ( !_fIsReadOnly )
|
|
DoRecovery();
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
ciDebugOut (( DEB_ERROR,
|
|
"Catalog initialization failed, CI exception = %08x\n",
|
|
e.GetErrorCode() ));
|
|
|
|
_statusMonitor.SetStatus( e.GetErrorCode() );
|
|
|
|
_strings.Empty();
|
|
_fileIdMap.Empty();
|
|
_propstoremgr.Empty();
|
|
_SecStore.Empty();
|
|
_PidTable.Empty();
|
|
_scopeTable.Empty();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( !_statusMonitor.IsOk() )
|
|
{
|
|
SCODE sc = _statusMonitor.GetStatus();
|
|
|
|
//
|
|
// If the status is corrupt, we'll blow away the catalog and start
|
|
// again. Note that a version change error at this point is due to
|
|
// a corrupt file, since normal version changes are detected earlier.
|
|
//
|
|
|
|
if ( ! ( IsCiCorruptStatus( sc ) || ( CI_INCORRECT_VERSION == sc ) ) )
|
|
_statusMonitor.ReportInitFailure();
|
|
|
|
THROW( CException( sc ) );
|
|
}
|
|
} //LokInit
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ForceMerge, public
|
|
//
|
|
// Synopsis: Forces a merge in the content index
|
|
//
|
|
// Arguments: [partid] -- Partition id
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
// 03-11-98 kitmanh Don't merge if catalog is
|
|
// opened for read-only. Also raise
|
|
// assertion fail to warn
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS CiCat::ForceMerge( PARTITIONID partid )
|
|
{
|
|
if ( _pStorage->IsReadOnly() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Cannot merge. Catalog is opened for read-only.\n" ));
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
|
|
XInterface<ICiManager> xCiManager;
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{
|
|
CLock lock(_mutex);
|
|
if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() )
|
|
{
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
else
|
|
{
|
|
_xCiManager->AddRef();
|
|
xCiManager.Set( _xCiManager.GetPointer() );
|
|
}
|
|
}
|
|
// -----------------------------------------------------
|
|
|
|
return xCiManager->ForceMerge( CI_MASTER_MERGE );
|
|
} //ForceMerge
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AbortMerge, public
|
|
//
|
|
// Synopsis: Stops a merge in the content index
|
|
//
|
|
// Arguments: [partid] -- Partition id
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS CiCat::AbortMerge( PARTITIONID partid )
|
|
{
|
|
XInterface<ICiManager> xCiManager;
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{
|
|
CLock lock(_mutex);
|
|
if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() )
|
|
{
|
|
return STATUS_TOO_LATE;
|
|
}
|
|
else
|
|
{
|
|
_xCiManager->AddRef();
|
|
xCiManager.Set( _xCiManager.GetPointer() );
|
|
}
|
|
}
|
|
// -----------------------------------------------------
|
|
|
|
return xCiManager->AbortMerge();
|
|
} //AbortMerge
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DumpWorkId, public
|
|
//
|
|
// Synopsis: Debug method to dump CI data
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [iid] -- Index id (0 if any)
|
|
// [pb] -- Return buffer
|
|
// [cb] -- Size of return buffer
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::DumpWorkId( WORKID wid, ULONG iid, BYTE * pb, ULONG cb )
|
|
{
|
|
|
|
Win4Assert( !"Backdoor not supported in framework" );
|
|
|
|
#if 0
|
|
if ( 0 != _pCci )
|
|
{
|
|
ULONG UNALIGNED * pul = (ULONG UNALIGNED *)pb;
|
|
*pul = CiDumpWorkId;
|
|
pul++;
|
|
*pul = wid;
|
|
|
|
pul++;
|
|
*pul = iid;
|
|
|
|
_pCci->BackDoor( cb, pb );
|
|
}
|
|
#endif // 0
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CreateContentIndex, public
|
|
//
|
|
// Synopsis: Creates a virgin content index.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CiCat::CreateContentIndex()
|
|
{
|
|
|
|
Win4Assert( !"Must Not Be Called" );
|
|
|
|
InitIf();
|
|
return IsStarted();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EmptyContentIndex, public
|
|
//
|
|
// Synopsis: Deletes content index (including storage)
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::EmptyContentIndex()
|
|
{
|
|
XInterface<ICiManager> xCiManager;
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{
|
|
CLock lock(_mutex);
|
|
if ( IsShuttingDown() || 0 == _xCiManager.GetPointer() )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
_xCiManager->AddRef();
|
|
xCiManager.Set( _xCiManager.GetPointer() );
|
|
}
|
|
}
|
|
// -----------------------------------------------------
|
|
|
|
SCODE sc = xCiManager->Empty();
|
|
if ( S_OK != sc )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"ICiManager::Empty failed with error (0x%X)\n",
|
|
sc ));
|
|
|
|
THROW( CException( sc ) );
|
|
}
|
|
} //EmptyContentIndex
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::_IsEligibleForFiltering, private
|
|
//
|
|
// Synopsis: Determine if a directory should be filtered
|
|
//
|
|
// Arguments: [pwcsPath] -- directory path name to be tested
|
|
// [ccPath] -- length of pwcsPath
|
|
//
|
|
// Returns: BOOL - TRUE if directory and contents should be filtered,
|
|
// FALSE otherwise
|
|
//
|
|
// History: 23 Jun 98 VikasMan Created
|
|
//
|
|
// Notes: This is the function which does the work and gets called from
|
|
// various versions of IsEligbleForFiltering. The path pwcsPath
|
|
// has to be all lowercase.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::_IsEligibleForFiltering( const WCHAR * pwcsPath, unsigned ccPath )
|
|
{
|
|
AssertLowerCase( pwcsPath, ccPath );
|
|
|
|
//
|
|
// These files can't be indexed or we'll either deadlock or never filter
|
|
// the files.
|
|
//
|
|
struct SUnfilterable
|
|
{
|
|
WCHAR const * pwcFile;
|
|
unsigned cwcFile;
|
|
};
|
|
|
|
static const SUnfilterable aUnfilterable[] =
|
|
{
|
|
{ L"\\classes.dat", 12 }, // classes hive
|
|
{ L"\\classes.dat.log", 16 }, // classes hive log
|
|
{ L"\\hiberfil.sys", 13 }, // the hibernation file
|
|
{ L"\\ntuser.dat", 11 }, // user hive
|
|
{ L"\\ntuser.dat.log", 15 }, // user hive log file
|
|
{ L"\\pagefile.sys", 13 }, // the pagefile
|
|
{ L"\\usrclass.dat", 13 }, // user classes hive file
|
|
{ L"\\usrclass.dat.log", 17 }, // user classes hive log file
|
|
};
|
|
|
|
const cUnfilterable = sizeof aUnfilterable / sizeof aUnfilterable[0];
|
|
|
|
//
|
|
// IMPORTANT OBSERVATION:
|
|
//
|
|
// All the entries above end in either 't', 'g', or 's'
|
|
//
|
|
|
|
static const WCHAR aLastLetter[] = L"tgs";
|
|
const cLastLetter = sizeof(aLastLetter)/sizeof(aLastLetter[0]) - 1;
|
|
|
|
for ( unsigned i = 0; i < cLastLetter; i++ )
|
|
if ( pwcsPath[ccPath-1] == aLastLetter[i] )
|
|
{
|
|
for ( unsigned j = 0; j < cUnfilterable; j++ )
|
|
{
|
|
SUnfilterable const & entry = aUnfilterable[ j ];
|
|
|
|
if ( ( ccPath > entry.cwcFile ) &&
|
|
( RtlEqualMemory( pwcsPath + ccPath - entry.cwcFile,
|
|
entry.pwcFile,
|
|
entry.cwcFile * sizeof WCHAR ) ) )
|
|
{
|
|
ciDebugOut(( DEB_IWARN,
|
|
"File %ws ineligible for filtering (unfilterable).\n",
|
|
pwcsPath ));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Nothing in a CATALOG.WCI directory can be indexed. That is metadata.
|
|
//
|
|
|
|
const unsigned CAT_DIR_LEN = sizeof CAT_DIR / sizeof CAT_DIR[0];
|
|
|
|
const WCHAR * wcsComponent = pwcsPath;
|
|
|
|
while ( wcsComponent = wcschr( wcsComponent + 1, L'\\' ) )
|
|
{
|
|
switch ( wcsComponent[1] )
|
|
{
|
|
case 'c':
|
|
Win4Assert( CAT_DIR[1] == L'c' );
|
|
if ( RtlEqualMemory( wcsComponent, CAT_DIR,
|
|
sizeof CAT_DIR - sizeof (WCHAR)) &&
|
|
( wcsComponent[ CAT_DIR_LEN-1 ] == L'\\' ||
|
|
wcsComponent[ CAT_DIR_LEN-1 ] == L'\0' ))
|
|
{
|
|
ciDebugOut(( DEB_IWARN,
|
|
"File %ws ineligible for filtering (ci metadata).\n",
|
|
pwcsPath ));
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case L'$':
|
|
Win4Assert( SETUP_DIR_PREFIX[1] == L'$' );
|
|
if ( RtlEqualMemory( wcsComponent, SETUP_DIR_PREFIX,
|
|
sizeof SETUP_DIR_PREFIX - sizeof (WCHAR) ) )
|
|
{
|
|
ciDebugOut(( DEB_IWARN,
|
|
"File %ws ineligible for filtering (nt setup directory).\n",
|
|
pwcsPath ));
|
|
return FALSE;
|
|
}
|
|
break;
|
|
} //switch
|
|
} //while
|
|
|
|
//
|
|
// skip indexing catalog directory.
|
|
//
|
|
|
|
if ( _CatDir.IsInScope( pwcsPath, ccPath ) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Nothing the admin told us not to index can be indexed. Duh!
|
|
//
|
|
|
|
BOOL fFound = _scopesIgnored.RegExFind( pwcsPath );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "%ws %ws\n", pwcsPath,
|
|
fFound? L"not indexed" : L" indexed" ));
|
|
|
|
return !fFound;
|
|
} //_IsEligibleForFiltering
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsEligibleForFiltering, public
|
|
//
|
|
// Synopsis: Determine if a directory should be filtered
|
|
//
|
|
// Arguments: [wcsDirPath] -- directory path name to be tested
|
|
//
|
|
// Returns: BOOL - TRUE if directory and contents should be filtered,
|
|
// FALSE otherwise
|
|
//
|
|
// History: 09 Feb 96 AlanW Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsEligibleForFiltering( WCHAR const* wcsDirPath )
|
|
{
|
|
CLowcaseBuf Buf( wcsDirPath );
|
|
|
|
return _IsEligibleForFiltering( Buf.Get(), Buf.Length() );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsEligibleForFiltering, public
|
|
//
|
|
// Synopsis: Determine if a directory should be filtered
|
|
//
|
|
// Arguments: [lcPath] -- directory path name to be tested
|
|
//
|
|
// Returns: BOOL - TRUE if directory and contents should be filtered,
|
|
// FALSE otherwise
|
|
//
|
|
// History: 17 Jul 96 AlanW Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsEligibleForFiltering( const CLowcaseBuf & lcPath )
|
|
{
|
|
return _IsEligibleForFiltering( lcPath.Get(), lcPath.Length() );
|
|
} //IsEligibleForFiltering
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsEligibleForFiltering, public
|
|
//
|
|
// Synopsis: Determine if a directory should be filtered
|
|
//
|
|
// Arguments: [lowerFunnyPath] -- directory path name to be tested
|
|
//
|
|
// Returns: BOOL - TRUE if directory and contents should be filtered,
|
|
// FALSE otherwise
|
|
//
|
|
// History: 23 Jun 98 VikasMan Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsEligibleForFiltering( const CLowerFunnyPath & lowerFunnyPath )
|
|
{
|
|
return _IsEligibleForFiltering( lowerFunnyPath.GetActualPath(),
|
|
lowerFunnyPath.GetActualLength() );
|
|
} //IsEligibleForFiltering
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::Update, public
|
|
//
|
|
// Synopsis: Updates an individual workid
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [partid] -- Partition id
|
|
// [volumeId] -- Volume id
|
|
// [usn] -- USN
|
|
// [action] -- Update or delete
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
// 07-May-97 SitaramR Usns
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CiCat::Update( WORKID wid,
|
|
PARTITIONID partid,
|
|
VOLUMEID volumeId,
|
|
USN usn,
|
|
ULONG action )
|
|
{
|
|
InitOrCreate();
|
|
|
|
if (wid != widInvalid)
|
|
{
|
|
Win4Assert( 0 != _xCiManager.GetPointer() );
|
|
BOOL fDelete = CI_DELETE_OBJ == action;
|
|
|
|
CDocumentUpdateInfo info( wid, volumeId, usn, fDelete );
|
|
return _xCiManager->UpdateDocument( &info );
|
|
}
|
|
return S_OK;
|
|
} //Update
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::Update
|
|
//
|
|
// Synopsis: Process usn notification
|
|
//
|
|
// Arguments: [lcaseFunnyPath] -- File path
|
|
// [fileId] -- File id
|
|
// [widParent] -- Parent workid
|
|
// [usn] -- Usn
|
|
// [pUsnVolume] -- Usn volume
|
|
// [fDeleted] -- Deleted file ?
|
|
// [pLock] -- If non-zero, this is the cicat lock
|
|
// and we have already verified the file
|
|
// isn't in the catalog yet.
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::Update(
|
|
const CLowerFunnyPath & lcaseFunnyPath,
|
|
FILEID fileId,
|
|
WORKID widParent,
|
|
USN usn,
|
|
CUsnVolume * pUsnVolume,
|
|
BOOL fDeleted,
|
|
CReleasableLock * pLock )
|
|
{
|
|
// NOTE: We do not need cimpersonatesystem here because this is a USN
|
|
// notification, so it is always local!
|
|
|
|
//
|
|
// Get rid of any remaining backslash
|
|
//
|
|
// override const here - unethical, but saves us a copy
|
|
BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash();
|
|
|
|
TRY
|
|
{
|
|
WORKID wid;
|
|
ULONG action = CI_UPDATE_OBJ;
|
|
BOOL fNew = FALSE;
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( IsShuttingDown() )
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
|
|
Win4Assert( 0 != _xCiManager.GetPointer() );
|
|
Win4Assert( fileIdInvalid != fileId );
|
|
|
|
// The caller knows for sure that the file doesn't exist in
|
|
// the fileid map, and called this under cicat lock.
|
|
|
|
if ( 0 != pLock )
|
|
{
|
|
wid = widInvalid;
|
|
Win4Assert( widInvalid == _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() ) );
|
|
}
|
|
else
|
|
{
|
|
wid = _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() );
|
|
}
|
|
|
|
if ( fDeleted && widInvalid == wid )
|
|
{
|
|
//
|
|
// This file is not yet known to us. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
Win4Assert( IsOnUsnVolume( lcaseFunnyPath.GetActualPath() ) );
|
|
|
|
if ( fDeleted )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "delete %#I64x %ws\n",
|
|
fileId, lcaseFunnyPath.GetActualPath() ));
|
|
action = CI_DELETE_OBJ;
|
|
|
|
_fileIdMap.LokDelete( fileId, wid );
|
|
_strings.LokDelete( 0, wid, TRUE, TRUE );
|
|
}
|
|
else if ( widInvalid == wid )
|
|
{
|
|
fNew = TRUE;
|
|
wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(),
|
|
fileId,
|
|
TRUE,
|
|
widParent );
|
|
|
|
#if CIDBG == 1
|
|
ciDebugOut(( DEB_ITRACE, "lokadded %#I64x %#x, '%ws'\n",
|
|
fileId, wid, lcaseFunnyPath.GetActualPath() ));
|
|
|
|
//
|
|
// Just a few lines above in LokLookupWid, we couldn't
|
|
// lookup the wid using the fileid map. If we can look
|
|
// it up now, something is broken.
|
|
//
|
|
|
|
WORKID widExisting = _fileIdMap.LokFind( fileId,
|
|
pUsnVolume->VolumeId() );
|
|
if ( widInvalid != widExisting )
|
|
{
|
|
Win4Assert( widInvalid != widExisting );
|
|
ciDebugOut(( DEB_ERROR, "wid %#x, widExisting %#x\n",
|
|
wid, widExisting ));
|
|
ciDebugOut(( DEB_ERROR, "Wid info:" ));
|
|
DebugPrintWidInfo( wid );
|
|
ciDebugOut(( DEB_ERROR, "WidExisting info:" ));
|
|
DebugPrintWidInfo( widExisting );
|
|
}
|
|
#endif // CIDBG == 1
|
|
|
|
//
|
|
// Add fileid -> wid map
|
|
//
|
|
|
|
_fileIdMap.LokAdd( fileId, wid, pUsnVolume->VolumeId() );
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"Added fileId %#I64x -> wid 0x%x mapping\n",
|
|
fileId, wid ));
|
|
}
|
|
|
|
if ( 0 != pLock )
|
|
pLock->Release();
|
|
}
|
|
|
|
SCODE sc = Update( wid,
|
|
GetPartition(),
|
|
pUsnVolume->VolumeId(),
|
|
usn,
|
|
action );
|
|
|
|
ciDebugOut(( DEB_USN,
|
|
"File %ws, fileId %#I64x, widParent 0x%x, usn %#I64x, fDelete %d, processed\n",
|
|
lcaseFunnyPath.GetActualPath(),
|
|
fileId,
|
|
widParent,
|
|
usn,
|
|
fDeleted ));
|
|
|
|
//
|
|
// Newly created files have pidLastSeenTime and pidAttrib initialized
|
|
// to 0 already in CStrings::LokAdd
|
|
//
|
|
|
|
if ( !fNew && !fDeleted && SUCCEEDED(sc))
|
|
{
|
|
FILETIME ftLastSeen;
|
|
RtlZeroMemory( &ftLastSeen, sizeof(ftLastSeen) );
|
|
CStorageVariant var( ftLastSeen );
|
|
|
|
XWritePrimaryRecord rec( _propstoremgr, wid );
|
|
|
|
SCODE scW = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidLastSeenTime,
|
|
var );
|
|
if ( FAILED( scW ) )
|
|
THROW( CException( scW ) );
|
|
|
|
PROPVARIANT propVar;
|
|
propVar.vt = VT_UI4;
|
|
propVar.ulVal = 0;
|
|
scW = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidAttrib,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if ( FAILED( scW ) )
|
|
THROW( CException( scW ) );
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
RETHROW();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
} //Update
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsBackupLikeNotification
|
|
//
|
|
// Synopsis: Tests if the change notification is like backup resetting the
|
|
// archive bit.
|
|
//
|
|
// Arguments: [ulAttribNew] - The current file attributes
|
|
// [ulAttribOld] - Previous file attributes that are stored
|
|
//
|
|
// Returns: TRUE if the change notification is due to backup archiving
|
|
// the file.
|
|
// FALSE o/w
|
|
//
|
|
// History: 5-13-96 srikants Created
|
|
//
|
|
// Notes: If the notification is a result of BACKUP turning off the
|
|
// archive bit:
|
|
// 1. The archive bit is turned OFF
|
|
// 2. The attribute mask MAY have the FILE_ATTRIBUTE_NORMAL
|
|
// turned on.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline BOOL IsBackupLikeNotification( ULONG ulAttribNew, ULONG ulAttribOld )
|
|
{
|
|
Win4Assert( (ulAttribNew & FILE_ATTRIBUTE_ARCHIVE) == 0 );
|
|
|
|
//
|
|
// See if the only difference between the old and the new attributes
|
|
// is in the FILE_ATTRIBUTE_NORMAL and FILE_ATTRIBUTE_ARCHIVE.
|
|
//
|
|
return ( (ulAttribNew ^ ulAttribOld) &
|
|
(~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ) == 0;
|
|
} //IsBackupLikeNotification
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsIgnoreNotification
|
|
//
|
|
// Synopsis: Tests if the current notification can be ignored
|
|
//
|
|
// Arguments: [wid] - Workid
|
|
// [funnyPath] - Full Path of the file
|
|
// [ulFileAttrib] - Current file attributes of the file
|
|
//
|
|
// Returns: TRUE if the notification can be ignored.
|
|
// FALSE if not.
|
|
//
|
|
// History: 5-17-96 srikants Created
|
|
//
|
|
// Notes: Programs like touch.exe don't update any attributes but just
|
|
// update the last write time. In addition to checking the
|
|
// archive bit, we also have to check the last write time.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsIgnoreNotification( WORKID wid, const CFunnyPath & funnyPath,
|
|
ULONG ulFileAttrib )
|
|
{
|
|
BOOL fIgnore = TRUE;
|
|
|
|
//
|
|
// See if the only difference with the existing attributes is
|
|
// archive bit. In that case, we don't have to refilter the doc.
|
|
//
|
|
|
|
XPrimaryRecord rec( _propstoremgr, wid );
|
|
|
|
PROPVARIANT propVar;
|
|
if ( !_propstoremgr.ReadProperty( rec.GetReference(), pidAttrib, propVar ) )
|
|
return FALSE;
|
|
|
|
ULONG ulOldAttrib = propVar.ulVal;
|
|
|
|
if ( !IsBackupLikeNotification(ulFileAttrib, ulOldAttrib) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Read the last "seen" time from the property store.
|
|
//
|
|
|
|
if ( ( !_propstoremgr.ReadProperty( rec.GetReference(), pidLastSeenTime, propVar ) ) ||
|
|
( VT_EMPTY == propVar.vt ) ||
|
|
( 0 == propVar.hVal.QuadPart ) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Retrieve the last write time from the file and then compare.
|
|
//
|
|
|
|
WIN32_FIND_DATA ffData;
|
|
if ( !GetFileAttributesEx( funnyPath.GetPath(), GetFileExInfoStandard, &ffData ) )
|
|
return FALSE;
|
|
|
|
return CompareFileTime( &ffData.ftLastWriteTime,
|
|
(FILETIME *) &propVar.hVal ) <= 0;
|
|
} //IsIgnoreNotification
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::Update
|
|
//
|
|
// Synopsis: Notifies content index about the change to the given document.
|
|
//
|
|
// Arguments: [lcaseFunnyPath] - Path of the document that changed.
|
|
// [fDeleted] - Set to TRUE if the document is deleted.
|
|
// [ftLastSeen] - Last seen time
|
|
// [ulFileAttrib] - File attributes to be written to prop store
|
|
//
|
|
// History: 1-18-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::Update( const CLowerFunnyPath & lcaseFunnyPath,
|
|
BOOL fDeleted,
|
|
FILETIME const &ftLastSeen,
|
|
ULONG ulFileAttrib )
|
|
{
|
|
//
|
|
// Don't filter catalog files
|
|
//
|
|
if ( ! IsEligibleForFiltering( lcaseFunnyPath ) )
|
|
return;
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
// override const here - unethical, but saves us a copy
|
|
BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash();
|
|
|
|
TRY
|
|
{
|
|
WORKID wid;
|
|
USN usn;
|
|
ULONG action;
|
|
BOOL fNew;
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( IsShuttingDown() )
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
|
|
Win4Assert( 0 != _xCiManager.GetPointer() );
|
|
|
|
Win4Assert( !IsOnUsnVolume( lcaseFunnyPath.GetActualPath() ) );
|
|
|
|
wid = _strings.LokFind( lcaseFunnyPath.GetActualPath() );
|
|
|
|
if ( fDeleted && widInvalid == wid )
|
|
{
|
|
//
|
|
// This file is not yet known to us. Nothing to do.
|
|
//
|
|
return;
|
|
}
|
|
|
|
usn = 1;
|
|
action = CI_UPDATE_OBJ;
|
|
fNew = FALSE;
|
|
|
|
if ( fDeleted )
|
|
{
|
|
usn = 0;
|
|
action = CI_DELETE_OBJ;
|
|
_strings.LokDelete( lcaseFunnyPath.GetActualPath(), wid );
|
|
}
|
|
else if ( widInvalid == wid )
|
|
{
|
|
fNew = TRUE;
|
|
wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(),
|
|
fileIdInvalid,
|
|
FALSE,
|
|
widInvalid,
|
|
ulFileAttrib,
|
|
& ftLastSeen );
|
|
}
|
|
}
|
|
|
|
Win4Assert( widInvalid != wid );
|
|
|
|
//
|
|
// BACKUP program just turns off the archive bit. We don't want to
|
|
// filter in that case.
|
|
//
|
|
BOOL fIgnore = FALSE;
|
|
if ( !fNew && !fDeleted && !(ulFileAttrib & FILE_ATTRIBUTE_ARCHIVE) )
|
|
{
|
|
fIgnore = IsIgnoreNotification( wid, lcaseFunnyPath, ulFileAttrib );
|
|
|
|
#if CIDBG==1
|
|
if ( fIgnore )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"Ignoring Archive Bit modification for (%ws)\n",
|
|
lcaseFunnyPath.GetActualPath() ));
|
|
}
|
|
#endif // CIDBG==1
|
|
}
|
|
|
|
SCODE sc = S_OK;
|
|
if ( !fIgnore )
|
|
sc = Update( wid, GetPartition(), CI_VOLID_USN_NOT_ENABLED, usn, action );
|
|
|
|
if ( !fNew && !fDeleted && SUCCEEDED(sc) )
|
|
{
|
|
CStorageVariant var( ftLastSeen );
|
|
|
|
XWritePrimaryRecord rec( _propstoremgr, wid );
|
|
|
|
sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidLastSeenTime,
|
|
var );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
|
|
Win4Assert( ulFileAttrib != 0xFFFFFFFF );
|
|
|
|
PROPVARIANT propVar;
|
|
propVar.vt = VT_UI4;
|
|
propVar.ulVal = ulFileAttrib;
|
|
sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidAttrib,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
RETHROW();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
} //Update
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AddDocuments
|
|
//
|
|
// Synopsis: Adds the given documents to the content index.
|
|
//
|
|
// Arguments: [docList] - List of documents.
|
|
//
|
|
// History: 3-19-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::AddDocuments( CDocList const & docList )
|
|
{
|
|
Win4Assert( 0 != _xCiManager.GetPointer() );
|
|
|
|
FILETIME ftLastSeen;
|
|
GetSystemTimeAsFileTime( &ftLastSeen );
|
|
|
|
CStorageVariant var( ftLastSeen );
|
|
|
|
for ( unsigned i = 0; i < docList.Count(); i++ )
|
|
{
|
|
CDocumentUpdateInfo info( docList.Wid(i),
|
|
docList.VolumeId(i),
|
|
docList.Usn(i),
|
|
FALSE );
|
|
SCODE sc = _xCiManager->UpdateDocument( &info );
|
|
|
|
// NTRAID#DB-NTBUG9-83752-2000/07/31-dlee cicat::AddDocuments ignores return code from CIManager::UpdateDocument
|
|
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
sc = _propstoremgr.WritePrimaryProperty( docList.Wid(i),
|
|
pidLastSeenTime,
|
|
var );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
}
|
|
}
|
|
} //AddDocuments
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CatalogState, public
|
|
//
|
|
// Synopsis: Compute and return catalog state
|
|
//
|
|
// Arguments: [cDocuments] -- Total number of documents in catalog
|
|
// [cPendingScans] -- Count of pending scans
|
|
// [fState] -- In-progress actions
|
|
//
|
|
// History: 04-18-96 KyleP Created
|
|
// 05-14-96 KyleP Added pending scans
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::CatalogState( ULONG & cDocuments,
|
|
ULONG & cPendingScans,
|
|
ULONG & fState )
|
|
{
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::CatalogState - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( IsStarted() )
|
|
{
|
|
cDocuments = _propstoremgr.CountRecordsInUse();
|
|
|
|
if ( IsReadOnly() )
|
|
{
|
|
fState = CI_STATE_READ_ONLY;
|
|
cPendingScans = 0;
|
|
}
|
|
else
|
|
{
|
|
ULONG ulInProgressScans, ulPendingScans;
|
|
_scanMgr.Count( ulInProgressScans, ulPendingScans );
|
|
|
|
ULONG ulInProgressUsnScans, ulPendingUsnScans;
|
|
_usnMgr.Count( ulInProgressUsnScans, ulPendingUsnScans );
|
|
|
|
cPendingScans = ulInProgressScans
|
|
+ ulPendingScans
|
|
+ ulInProgressUsnScans
|
|
+ ulPendingUsnScans;
|
|
|
|
if ( ulInProgressScans > 0 || ulInProgressUsnScans > 0 )
|
|
fState = CI_STATE_SCANNING;
|
|
else
|
|
fState = 0;
|
|
|
|
if ( !_fRecoveryCompleted )
|
|
fState |= CI_STATE_RECOVERING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cDocuments = 0;
|
|
cPendingScans = 0;
|
|
fState = 0;
|
|
}
|
|
} //CatalogState
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EnumerateProperty, public
|
|
//
|
|
// Synopsis: Iterates all properties
|
|
//
|
|
// Arguments: [ps] -- Property returned here
|
|
// [cbInCache] -- Number of bytes / record allocated for this property
|
|
// [type] -- Property type.
|
|
// [storeLevel]-- Property store level.
|
|
// [fIsModifiable] -- Can the meta data for this prop be modified?
|
|
// [iBmk] -- Bookmark for iteration. Begin at 0.
|
|
//
|
|
// Returns: TRUE if another property was returned.
|
|
//
|
|
// History: 11-Feb-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::EnumerateProperty( CFullPropSpec & ps, unsigned & cbInCache,
|
|
ULONG & type, DWORD & dwStoreLevel,
|
|
BOOL & fIsModifiable, unsigned & iBmk )
|
|
{
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::EnumerateProperty - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
//
|
|
// Cheat a little here. I probably should call CQCat...
|
|
//
|
|
|
|
BOOL fOk;
|
|
|
|
//
|
|
// Note that the 'special' property sets (query\rank, etc.) other
|
|
// than storage are excluded. They can never be in the property cache.
|
|
//
|
|
|
|
if ( iBmk <= PID_STG_MAX - 2 )
|
|
{
|
|
static const GUID guidStorage = PSGUID_STORAGE;
|
|
|
|
ps.SetPropSet( guidStorage );
|
|
ps.SetProperty( iBmk + 2 );
|
|
iBmk++;
|
|
fOk = TRUE;
|
|
}
|
|
else
|
|
{
|
|
iBmk -= (PID_STG_MAX - 2 + 1); // Want 0-based count.
|
|
|
|
fOk = _PidTable.EnumerateProperty( ps, iBmk );
|
|
|
|
iBmk += (PID_STG_MAX - 2 + 1); // Back to global count.
|
|
}
|
|
|
|
if ( fOk )
|
|
{
|
|
PROPID pid = PropertyToPropId( ps, FALSE );
|
|
|
|
if ( pidInvalid == pid )
|
|
return FALSE;
|
|
|
|
cbInCache = _propstoremgr.Size( pid );
|
|
type = _propstoremgr.Type( pid );
|
|
dwStoreLevel = _propstoremgr.StoreLevel( pid );
|
|
fIsModifiable = _propstoremgr.CanBeModified( pid );
|
|
}
|
|
|
|
return fOk;
|
|
} //EnumerateProperty
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::BeginCacheTransaction, public
|
|
//
|
|
// Synopsis: Opens a cache transaction. Aborts any existing open transaction.
|
|
//
|
|
// Returns: Token of new transaction.
|
|
//
|
|
// History: 20-Jun-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
ULONG_PTR CiCat::BeginCacheTransaction()
|
|
{
|
|
//
|
|
// Don't let anyone in until the long initialization is completed.
|
|
//
|
|
|
|
if ( !_fInitialized )
|
|
_evtInitialized.Wait();
|
|
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::BeginCacheTransaction - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
return _propstoremgr.BeginTransaction();
|
|
} //BeginCacheTransaction
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SetupCache, public
|
|
//
|
|
// Synopsis: Add/Modify/Remove property from cache.
|
|
//
|
|
// Arguments: [ps] -- Property spec
|
|
// [vt] -- Data type of value
|
|
// [cbMaxLen] -- Soft-maximum length of value. 0 --> delete
|
|
// [ulToken] -- Token of active transaction
|
|
//
|
|
// History: 17-Jan-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::SetupCache( CFullPropSpec const & ps,
|
|
ULONG vt,
|
|
ULONG cbMaxLen,
|
|
ULONG_PTR ulToken,
|
|
BOOL fCanBeModified,
|
|
DWORD dwStoreLevel )
|
|
{
|
|
//
|
|
// Don't let anyone in until the long initialization is completed.
|
|
//
|
|
|
|
if ( !_fInitialized )
|
|
_evtInitialized.Wait();
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::SetupCache - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
PROPID pid = PropertyToPropId( ps, TRUE );
|
|
|
|
if ( pid != pidPath && pid != pidLastSeenTime && pid != pidAttrib &&
|
|
pid != pidVirtualPath && pid != pidSecurity &&
|
|
pid != pidParentWorkId && pid != pidSecondaryStorage &&
|
|
pid != pidFileIndex && pid != pidVolumeId &&
|
|
pid != pidSize && pid != pidWriteTime )
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
_propstoremgr.Setup( pid, vt, cbMaxLen, ulToken, fCanBeModified, dwStoreLevel );
|
|
|
|
if ( 0 == cbMaxLen ) // Delete
|
|
DeleteUserProperty( ps );
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Attempt to modify caching of required property ignored!\n" ));
|
|
//THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
}
|
|
} //SetupCache
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EndCacheTransaction, public
|
|
//
|
|
// Synopsis: Closes a cache transaction.
|
|
//
|
|
// Arguments: [ulToken] -- Token of transaction
|
|
// [fCommit] -- TRUE --> Commit changes
|
|
//
|
|
// History: 20-Jun-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::EndCacheTransaction( ULONG_PTR ulToken, BOOL fCommit )
|
|
{
|
|
//
|
|
// Don't let anyone in until the long initialization is completed.
|
|
//
|
|
|
|
if ( !_fInitialized )
|
|
_evtInitialized.Wait();
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::EndCacheTransaction - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
_propstoremgr.EndTransaction( ulToken, fCommit, pidSecurity, pidVirtualPath );
|
|
} //EndCacheTransaction
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::StoreValue, public
|
|
//
|
|
// Synopsis: Stores value in property cache
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [ps] -- Property spec
|
|
// [var] -- Value to store
|
|
//
|
|
// Returns: TRUE if the property is in the property store schema
|
|
// FALSE if the property is not in the property store schema
|
|
//
|
|
// Notes: Throws on exceptions.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::StoreValue( WORKID wid,
|
|
CFullPropSpec const & ps,
|
|
CStorageVariant const & var )
|
|
{
|
|
if ( !IsStarted() && !IsShuttingDown() )
|
|
return FALSE;
|
|
|
|
PROPID pid = PropertyToPropId( ps, FALSE );
|
|
|
|
if ( pid != pidInvalid && wid != widInvalid )
|
|
{
|
|
//
|
|
// HACK #721: On NTFS volumes, store LastChange time as LastSeen time.
|
|
// We don't use LastSeen for NTFS, and we need LastChange.
|
|
// This trick saves 1 DWORD per primary record.
|
|
//
|
|
// Note that only NTFS volumes will ever send a LastChange
|
|
// time.
|
|
//
|
|
|
|
if ( pidChangeTime == pid )
|
|
pid = pidLastSeenTime;
|
|
|
|
SCODE sc = _propstoremgr.WriteProperty( wid, pid, var );
|
|
|
|
if (FAILED(sc))
|
|
HandleError( sc );
|
|
|
|
if ( S_OK != sc )
|
|
{
|
|
if ( pidSize == pid )
|
|
{
|
|
ciDebugOut(( DEB_FORCE, "cicat: can't store pidSize: %#x\n", sc ));
|
|
}
|
|
}
|
|
|
|
if ( FAILED( sc ) )
|
|
THROW( CException( sc ) );
|
|
|
|
return (S_OK == sc);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchValue, public
|
|
//
|
|
// Synopsis: Retrieves value from property cache
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [pid] -- Property id
|
|
// [pbData] -- Value returned here
|
|
// [pcb] -- On input, size in bytes of [pbData]. On
|
|
// output, size needed to store value.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::FetchValue( WORKID wid,
|
|
PROPID pid,
|
|
PROPVARIANT * pbData,
|
|
unsigned * pcb )
|
|
{
|
|
if ( !IsStarted() && !IsShuttingDown() )
|
|
return FALSE;
|
|
else
|
|
return _propstoremgr.ReadProperty( wid, pid, pbData, pcb );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchValue, public
|
|
//
|
|
// Synopsis: Retrieves value from property cache. Uses CoTaskMemAlloc
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [pid] -- Property id
|
|
// [var] -- Value returned here
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::FetchValue( WORKID wid,
|
|
CFullPropSpec const & ps,
|
|
PROPVARIANT & var )
|
|
{
|
|
if ( !IsStarted() && !IsShuttingDown() )
|
|
return FALSE;
|
|
else
|
|
{
|
|
PROPID pid = PropertyToPropId( ps, FALSE );
|
|
|
|
if ( pidInvalid != pid )
|
|
return _propstoremgr.ReadProperty( wid, pid, var );
|
|
else
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchValue, public
|
|
//
|
|
// Synopsis: Retrieves value from property cache. Uses pre-existing
|
|
// property record.
|
|
//
|
|
// Arguments: [pRec] -- Property record
|
|
// [pid] -- Property id
|
|
// [pbData] -- Value returned here
|
|
// [pcb] -- On input, size in bytes of [pbData]. On
|
|
// output, size needed to store value.
|
|
//
|
|
// Returns: TRUE if property exists (and was returned).
|
|
//
|
|
// History: 03-Apr-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::FetchValue( CCompositePropRecord * pRec,
|
|
PROPID pid,
|
|
PROPVARIANT * pbData,
|
|
unsigned * pcb )
|
|
{
|
|
if ( (!IsStarted() && !IsShuttingDown()) || 0 == pRec )
|
|
return FALSE;
|
|
else
|
|
return _propstoremgr.ReadProperty( *pRec, pid, pbData, pcb );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchValue, public
|
|
//
|
|
// Synopsis: Retrieves value from property cache. Uses pre-existing
|
|
// property record.
|
|
//
|
|
// Arguments: [Rec] -- Property record
|
|
// [pid] -- Property id
|
|
// [pbData] -- Value returned here
|
|
// [pbExtra] -- Where to put data that won't fit in pbData
|
|
// [pcbExtra]-- On input, size in bytes of [pbExtra]. On
|
|
// output, size needed to store value.
|
|
//
|
|
// Returns: TRUE if property exists (and was returned).
|
|
//
|
|
// History: 02-Feb-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::FetchValue( CCompositePropRecord & Rec,
|
|
PROPID pid,
|
|
PROPVARIANT * pvData,
|
|
BYTE * pbExtra,
|
|
unsigned * pcbExtra )
|
|
{
|
|
if ( (!IsStarted() && !IsShuttingDown()) || 0 == &Rec )
|
|
return FALSE;
|
|
else
|
|
return _propstoremgr.ReadProperty( Rec, pid, *pvData, pbExtra, pcbExtra );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchValue, public
|
|
//
|
|
// Synopsis: Retrieves value from property cache. Uses pre-existing
|
|
// property record, and allocations are done with CoTaskMem
|
|
//
|
|
// Arguments: [pRec] -- Property record
|
|
// [pid] -- Property id
|
|
// [var] -- Value returned here
|
|
//
|
|
// Returns: TRUE if property exists (and was returned).
|
|
//
|
|
// History: 19-Dec-97 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::FetchValue( CCompositePropRecord * pRec,
|
|
PROPID pid,
|
|
PROPVARIANT & var )
|
|
{
|
|
if ( (!IsStarted() && !IsShuttingDown()) || 0 == pRec )
|
|
return FALSE;
|
|
else
|
|
return _propstoremgr.ReadProperty( *pRec, pid, var );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::OpenValueRecord, public
|
|
//
|
|
// Synopsis: Opens property record, which can be used for multiple
|
|
// fetch operations.
|
|
//
|
|
// Arguments: [wid] -- WorkId of top-level record to open.
|
|
// [pb] -- Storage for record
|
|
//
|
|
// Returns: Pointer to record, or zero if invalid.
|
|
//
|
|
// History: 03-Apr-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CCompositePropRecord * CiCat::OpenValueRecord( WORKID wid, BYTE * pb )
|
|
{
|
|
if ( !IsStarted() && !IsShuttingDown() )
|
|
return 0;
|
|
else
|
|
return _propstoremgr.OpenRecord( wid, pb );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CloseValueRecord, public
|
|
//
|
|
// Synopsis: Closes property record.
|
|
//
|
|
// Arguments: [pRec] -- Pointer to record. 0 is legal.
|
|
//
|
|
// History: 03-Apr-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::CloseValueRecord( CCompositePropRecord * pRec )
|
|
{
|
|
if ( 0 != pRec )
|
|
_propstoremgr.CloseRecord( pRec );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::StoreSecurity, public
|
|
//
|
|
// Synopsis: Determine SDID value and save in property cache.
|
|
//
|
|
// Arguments: [wid] -- Workid of file to store SDID for
|
|
// [pSD] -- a pointer to the file's security descriptor
|
|
// [cbSD] -- the size in bytes of the security descriptor
|
|
//
|
|
// History: 02 Feb 96 Alanw Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::StoreSecurity( WORKID wid,
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
ULONG cbSD )
|
|
{
|
|
Win4Assert ( IsStarted() || IsShuttingDown() );
|
|
|
|
PROPVARIANT var;
|
|
var.vt = VT_UI4;
|
|
|
|
if ( 0 != cbSD )
|
|
var.ulVal = _SecStore.LookupSDID( pSD, cbSD );
|
|
else
|
|
var.ulVal = sdidNull;
|
|
|
|
SCODE sc = _propstoremgr.WritePrimaryProperty( wid,
|
|
pidSecurity,
|
|
*(CStorageVariant const *)(ULONG_PTR)&var );
|
|
|
|
if (FAILED(sc))
|
|
HandleError( sc );
|
|
|
|
return (S_OK == sc);
|
|
} //StoreSecurity
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FetchSDID, public
|
|
//
|
|
// Synopsis: Retrieve SDID value for a file
|
|
//
|
|
// Arguments: [pRec] -- Property record (may be NULL)
|
|
// [wid] -- Workid of file for which to fetch SDID
|
|
//
|
|
// History: 02 Feb 96 Alanw Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SDID CiCat::FetchSDID( CCompositePropRecord * pRec, WORKID wid )
|
|
{
|
|
PROPVARIANT var;
|
|
unsigned cbVar = sizeof var;
|
|
|
|
BOOL fOk;
|
|
if ( 0 == pRec )
|
|
fOk = FetchValue( wid, pidSecurity, &var, &cbVar );
|
|
else
|
|
fOk = FetchValue( pRec, pidSecurity, &var, &cbVar );
|
|
|
|
// if the property isn't in the schema, we don't care about security
|
|
|
|
if ( !fOk )
|
|
return sdidNull;
|
|
|
|
// if there isn't a value yet, assume no access
|
|
|
|
if ( VT_UI4 != var.vt )
|
|
return sdidInvalid;
|
|
|
|
return var.ulVal;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AccessCheck, public
|
|
//
|
|
// Synopsis: Perform an access check given an SDID
|
|
//
|
|
// Arguments: [sdid] -- SDID of file to be checked
|
|
// [hToken] -- token handle for client
|
|
// [am] -- requested access
|
|
// [fGranted] -- if successful check, whether access was granted
|
|
//
|
|
// Returns: BOOL - TRUE if the access check was successfully done.
|
|
//
|
|
// History: 02 Feb 96 Alanw Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::AccessCheck( SDID sdid,
|
|
HANDLE hToken,
|
|
ACCESS_MASK am,
|
|
BOOL & fGranted )
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
return _SecStore.AccessCheck( sdid, hToken, am, fGranted );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::PersistMaxUSNs, private
|
|
//
|
|
// Synopsis: Writes the maximum USN processed to the scope table, called
|
|
// at shutdown.
|
|
//
|
|
// History: 11-18-98 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::PersistMaxUSNs()
|
|
{
|
|
//
|
|
// Do nothing for read-only catalogs and error cases.
|
|
//
|
|
|
|
if ( IsReadOnly() || eStarted != _state )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "not persisting max usn...\n" ));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the persistent scope table so the high-water USN is
|
|
// persisted. This is needed if lots of usns have been processed
|
|
// but no documents have been indexed, so ProcessChangesFlush hasn't
|
|
// been called to persist the scope table.
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE, "flushinfolist count before %d\n",
|
|
_usnFlushInfoList.Count() ));
|
|
|
|
_usnMgr.GetMaxUSNs( _usnFlushInfoList );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "flushinfolist count after %d\n",
|
|
_usnFlushInfoList.Count() ));
|
|
|
|
SerializeChangesInfo();
|
|
} //PersistMaxUsns
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ShutdownPhase1, public
|
|
//
|
|
// Synopsis: Dismounts the catalog by stopping all scan/update activity.
|
|
//
|
|
// History: 1-30-96 srikants Created
|
|
//
|
|
// Notes: In phase 1 of shutdown, all activity sent to the framework
|
|
// is disabled. The framework can still call the property store.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ShutdownPhase1()
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "ShutdownPhase1 %ws\n", _xName.Get() ));
|
|
|
|
TRY
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
TRY
|
|
{
|
|
PersistMaxUSNs();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
// Ignore it if we can't persist the max usns
|
|
// We'll just index more next time.
|
|
}
|
|
END_CATCH
|
|
|
|
// ===================================================
|
|
{
|
|
CLock lock(_mutex);
|
|
_state = eShutdownPhase1;
|
|
}
|
|
// ===================================================
|
|
|
|
_evtInitialized.Set();
|
|
_evtPh2Init.Set();
|
|
|
|
_strings.Abort();
|
|
|
|
//
|
|
// Initiate the shutdown for operations that may take a while.
|
|
//
|
|
|
|
_scanMgr.InitiateShutdown();
|
|
_usnMgr.InitiateShutdown();
|
|
_notify.InitiateShutdown();
|
|
_workMan.AbortWorkItems();
|
|
_scanMgr.WaitForShutdown();
|
|
_usnMgr.WaitForShutdown();
|
|
_notify.WaitForShutdown();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::Shutdown failed with error 0x%X\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //ShutdownPhase1
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ShutdownPhase2, public
|
|
//
|
|
// Synopsis: Finishes catalog dismount
|
|
//
|
|
// History: 1-30-96 srikants Created
|
|
//
|
|
// Notes: Called after framework is stopped.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ShutdownPhase2()
|
|
{
|
|
TRY
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
// ===================================================
|
|
{
|
|
CLock lock(_mutex);
|
|
Win4Assert( eShutdownPhase1 == _state );
|
|
_state = eShutdown;
|
|
}
|
|
// ===================================================
|
|
|
|
// Tell the property store we're in backedup mode and shutdown
|
|
// cleanly.
|
|
|
|
TRY
|
|
{
|
|
if ( !_propstoremgr.IsBackedUpMode() )
|
|
_propstoremgr.MarkBackedUpMode();
|
|
|
|
_propstoremgr.Shutdown();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::ShutdownPhase2: nested try block failed with error 0x%X\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
|
|
if ( _pStorage )
|
|
{
|
|
_strings.Shutdown();
|
|
_fileIdMap.Shutdown();
|
|
delete _pStorage;
|
|
_pStorage = 0;
|
|
}
|
|
|
|
_workMan.WaitForDeath();
|
|
|
|
CLock lock(_mutex);
|
|
|
|
_xCiManager.Free();
|
|
_xAdviseStatus.Free();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::ShutdownPhase2 failed with error 0x%X\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //ShutdownPhase2
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::~CiCat, public
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 10-Mar-92 BartoszM Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CiCat::~CiCat ()
|
|
{
|
|
ciDebugOut(( DEB_WARN, "CiCat::~CiCat: %ws\n", _xName.GetPointer() ));
|
|
|
|
if ( !IsShutdown() )
|
|
{
|
|
ShutdownPhase1();
|
|
ShutdownPhase2();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::Touch, public
|
|
//
|
|
// Synopsis: Marks a file as seen (so it appears to the catalog to have
|
|
// been recently saved).
|
|
//
|
|
// Arguments: [wid] - wid of file to be "touched"
|
|
// [eType] - Seen array type
|
|
//
|
|
// History: 10-Mar-92 BartoszM Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::Touch ( WORKID wid, ESeenArrayType eType )
|
|
{
|
|
CLock lock(_mutex);
|
|
Win4Assert ( IsInit() );
|
|
_strings.LokSetSeen ( wid, eType );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::WorkIdToPath, public
|
|
//
|
|
// Synopsis: Returns a path for a document given its work id, as funny path
|
|
//
|
|
// Arguments: [wid] - the work id of the document
|
|
// [funnyPath] - buffer to copy the path
|
|
//
|
|
// Returns: 0 if string not found ELSE
|
|
// Count of total chars in funnyPath
|
|
//
|
|
// History: 21-May-98 VikasMan Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
unsigned CiCat::WorkIdToPath ( WORKID wid, CFunnyPath& funnyPath )
|
|
{
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToPath - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
Win4Assert( wid != widInvalid );
|
|
|
|
unsigned cc = _strings.Find( wid, funnyPath );
|
|
|
|
#if CIDBG == 1
|
|
if ( cc > 0 )
|
|
ciDebugOut(( DEB_ITRACE, "wid %d (0x%x) --> %.*ws\n", wid, wid,
|
|
cc, funnyPath.GetActualPath() ));
|
|
#endif
|
|
return cc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::WorkIdToAccuratePath, public
|
|
//
|
|
// Synopsis: Returns a path for a document given its work id, as funny path.
|
|
// This variant uses fileid --> path mappings instead of trusting
|
|
// the property store.
|
|
//
|
|
// Arguments: [wid] - the work id of the document
|
|
// [funnyPath] - buffer to copy the path
|
|
//
|
|
// Returns: 0 if string not found ELSE
|
|
// Count of total chars in funnyPath
|
|
//
|
|
// History: 31-Dec-1998 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
unsigned CiCat::WorkIdToAccuratePath ( WORKID wid, CLowerFunnyPath& funnyPath )
|
|
{
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToPath - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
Win4Assert( wid != widInvalid );
|
|
|
|
//
|
|
// First try to get the path based on file id and volume id.
|
|
// This only works for files on USN volumes.
|
|
//
|
|
|
|
unsigned cc;
|
|
VOLUMEID volumeId;
|
|
FILEID fileId;
|
|
|
|
CCompositePropRecord PropRec( wid, _propstoremgr );
|
|
|
|
if ( PropertyRecordToFileId( PropRec, fileId, volumeId ) )
|
|
{
|
|
// PropertyRecordToFileId doesn't return a fileid without a volumeid
|
|
|
|
Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId );
|
|
|
|
cc = FileIdToPath( fileId, volumeId, funnyPath );
|
|
}
|
|
else
|
|
cc = _strings.Find( PropRec, funnyPath );
|
|
|
|
#if CIDBG == 1
|
|
if ( cc > 0 )
|
|
ciDebugOut(( DEB_ITRACE, "wid %d (0x%x) --> %.*ws\n", wid, wid,
|
|
cc, funnyPath.GetActualPath() ));
|
|
#endif
|
|
return cc;
|
|
} // WorkIdToAccuratePath
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::MarkUnReachable
|
|
//
|
|
// Synopsis: Marks the given wid currently unreachable.
|
|
//
|
|
// Arguments: [wid] - WORKID
|
|
//
|
|
// History: 5-21-96 srikants Created
|
|
//
|
|
// Notes: We write MAXTIME as the last seen time and this will be used
|
|
// as a special value during scanning.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::MarkUnReachable ( WORKID wid )
|
|
{
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::MarkUnReachable - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
Win4Assert( wid != widInvalid );
|
|
|
|
FILETIME ft;
|
|
FillMaxTime(ft);
|
|
CStorageVariant var( ft );
|
|
SCODE sc = _propstoremgr.WritePrimaryProperty( wid, pidLastSeenTime, var );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
|
|
//
|
|
// Set the SDID to avoid showing the file in case access was revoked.
|
|
//
|
|
var.SetUI4( sdidInvalid );
|
|
sc = _propstoremgr.WritePrimaryProperty( wid, pidSecurity, var );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::PathToWorkId, public
|
|
//
|
|
// Synopsis: Returns a workid for a document given its path.
|
|
//
|
|
// Arguments: [lcaseFunnyPath] -- Fully qualified path
|
|
// [fCreate] -- if TRUE, create workid for path if it
|
|
// doesn't already have one.
|
|
// [fNew] -- set to TRUE if a new workid is created
|
|
// [pftLastSeen] -- If non zero pointer, will have the time of
|
|
// the file last seen. If the last seen time is
|
|
// not known, or this file is being seen for
|
|
// the first time, the value will be set to zero.
|
|
// [eType] -- Seen array type
|
|
// [fileId] -- fileid if known or fileidInvalid
|
|
// [widParent] -- Parent workid
|
|
// [fGuaranteedNew] -- TRUE if the caller is under cicat lock
|
|
// and has already checked that the file
|
|
// is not known yet.
|
|
//
|
|
// Returns: Workid.
|
|
//
|
|
// History: 10-Jan-96 KyleP Added header
|
|
// 06-Aug-97 EmilyB Added fCreate parameter
|
|
// 10-Aug-97 EmilyB Updated to create wids for physical drive
|
|
// roots (so they can appear in scope
|
|
// hash table)
|
|
// 26-Mar-98 KitmanH If the catalog is r/o, don't LokAdd even
|
|
// though the file is not found on disk, just
|
|
// return widInvalid
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
WORKID CiCat::PathToWorkId (
|
|
const CLowerFunnyPath & lcaseFunnyPath,
|
|
BOOL fCreate,
|
|
BOOL & fNew,
|
|
FILETIME * pftLastSeen,
|
|
ESeenArrayType eType,
|
|
FILEID fileId,
|
|
WORKID widParent,
|
|
BOOL fGuaranteedNew )
|
|
{
|
|
if ( lcaseFunnyPath.IsRemote() )
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
return PathToWorkIdInternal( lcaseFunnyPath,
|
|
fCreate,
|
|
fNew,
|
|
pftLastSeen,
|
|
eType,
|
|
fileId,
|
|
widParent,
|
|
fGuaranteedNew );
|
|
}
|
|
else
|
|
{
|
|
return PathToWorkIdInternal( lcaseFunnyPath,
|
|
fCreate,
|
|
fNew,
|
|
pftLastSeen,
|
|
eType,
|
|
fileId,
|
|
widParent,
|
|
fGuaranteedNew );
|
|
}
|
|
}
|
|
|
|
WORKID CiCat::PathToWorkIdInternal(
|
|
const CLowerFunnyPath & lcaseFunnyPath,
|
|
BOOL fCreate,
|
|
BOOL & fNew,
|
|
FILETIME * pftLastSeen,
|
|
ESeenArrayType eType,
|
|
FILEID fileId,
|
|
WORKID widParent,
|
|
BOOL fGuaranteedNew )
|
|
{
|
|
//
|
|
// Don't let anyone in until the long initialization is completed.
|
|
//
|
|
if ( !_fInitialized )
|
|
_evtInitialized.Wait();
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::PathToWorkId - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
if ( !IsEligibleForFiltering( lcaseFunnyPath ) )
|
|
return widInvalid;
|
|
|
|
// override const here - unethical, but saves us a copy
|
|
BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash();
|
|
|
|
#if CIDBG == 1
|
|
//
|
|
// Check to see if the input path name contains an 8.3 short name
|
|
//
|
|
if (lcaseFunnyPath.IsShortPath( ))
|
|
{
|
|
ciDebugOut(( DEB_IWARN,
|
|
"PathToWorkId: possible shortname path %ws\n",
|
|
lcaseFunnyPath.GetActualPath() ));
|
|
}
|
|
#endif CIDBG
|
|
|
|
TRY
|
|
{
|
|
// =========================================================
|
|
CLock lock ( _mutex );
|
|
|
|
BOOL fUsnVolume = VolumeSupportsUsns( lcaseFunnyPath.GetActualPath()[0] );
|
|
WORKID wid;
|
|
|
|
if ( fUsnVolume )
|
|
{
|
|
if ( fGuaranteedNew )
|
|
{
|
|
Win4Assert( widInvalid == LokLookupWid( lcaseFunnyPath, fileId ) );
|
|
wid = widInvalid;
|
|
}
|
|
else
|
|
{
|
|
wid = LokLookupWid( lcaseFunnyPath, fileId );
|
|
}
|
|
|
|
// If the path was deleted, give up now and return a bogus wid.
|
|
|
|
if ( fileIdInvalid == fileId )
|
|
return widInvalid;
|
|
}
|
|
else
|
|
{
|
|
wid = _strings.LokFind( lcaseFunnyPath.GetActualPath() );
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE, "pathtoworkid, fCreate %d, wid %#x, fileid %#I64x\n",
|
|
fCreate, wid, fileId ));
|
|
|
|
if ( wid != widInvalid )
|
|
{
|
|
_strings.LokSetSeen( wid, eType );
|
|
if ( pftLastSeen )
|
|
fNew = !_strings.Find( wid, *pftLastSeen );
|
|
else
|
|
fNew = FALSE;
|
|
}
|
|
else if ( fCreate )
|
|
{
|
|
if ( !IsReadOnly() )
|
|
{
|
|
wid = _strings.LokAdd( lcaseFunnyPath.GetActualPath(),
|
|
fileId,
|
|
fUsnVolume,
|
|
widParent );
|
|
|
|
if ( fUsnVolume )
|
|
_fileIdMap.LokAdd( fileId,
|
|
wid,
|
|
MapPathToVolumeId( lcaseFunnyPath.GetActualPath() ) );
|
|
|
|
if ( pftLastSeen )
|
|
RtlZeroMemory( pftLastSeen, sizeof FILETIME );
|
|
|
|
fNew = TRUE;
|
|
}
|
|
else
|
|
wid = widInvalid;
|
|
}
|
|
else
|
|
{
|
|
if ( pftLastSeen )
|
|
RtlZeroMemory( pftLastSeen, sizeof FILETIME );
|
|
fNew = FALSE;
|
|
}
|
|
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
|
|
Win4Assert( fCreate || !fNew );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "%ws --> wid %d (0x%x)\n", lcaseFunnyPath.GetActualPath(), wid, wid ));
|
|
// =========================================================
|
|
|
|
return wid;
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
if ( fRemoveBackSlash )
|
|
{
|
|
// override const here - unethical, but saves us a copy
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
}
|
|
RETHROW();
|
|
}
|
|
END_CATCH
|
|
|
|
// Make the compiler happy
|
|
|
|
Win4Assert( !"unused codepath" );
|
|
return widInvalid;
|
|
} //PathToWorkId
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ComputeRelevantWords, public
|
|
//
|
|
// Synopsis: Computes and returns relevant words for a set of wids
|
|
//
|
|
// Arguments: [cRows] -- # of rows to compute and in pwid array
|
|
// [cRW] -- # of rw per wid
|
|
// [pwid] -- array of wids to work over
|
|
// [partid] -- partition
|
|
//
|
|
// History: 10-May-94 v-dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CRWStore * CiCat::ComputeRelevantWords(ULONG cRows,ULONG cRW,
|
|
WORKID *pwid,PARTITIONID partid)
|
|
{
|
|
Win4Assert( !"Not supported in Framework" );
|
|
|
|
return 0;
|
|
} //ComputeRelevantWords
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RetrieveRelevantWords, public
|
|
//
|
|
// Synopsis: Retrieves relevant words already computed
|
|
//
|
|
// Arguments: [fAcquire] -- TRUE if ownership is transferred.
|
|
// [partid] -- partition
|
|
//
|
|
// History: 10-May-94 v-dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CRWStore * CiCat::RetrieveRelevantWords(BOOL fAcquire,PARTITIONID partid)
|
|
{
|
|
Win4Assert( !"Not supported in Framework" );
|
|
return 0;
|
|
} //RetrieveRelevantWords
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::PidMapToPidRemap, public
|
|
//
|
|
// Synopsis: Converts a pidMapperArray into a pidRemapper
|
|
//
|
|
// Arguments: [pidMap] -- a pid mapper to convert into a pid remapper
|
|
// [pidRemap] -- the converted pid remapper;
|
|
//
|
|
// History: 01-Mar-95 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CiCat::PidMapToPidRemap( const CPidMapper & pidMap,
|
|
CPidRemapper & pidRemap )
|
|
{
|
|
//
|
|
// Rebuild the pidRemapper
|
|
//
|
|
pidRemap.ReBuild( pidMap );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CiState, public
|
|
//
|
|
// Synopsis: Returns state of downlevel CI
|
|
//
|
|
// Arguments: [state] -- buffer to return state into
|
|
//
|
|
// History: 14-Dec-95 DwightKr Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define setState( field, value ) \
|
|
if ( state.cbStruct >= ( offsetof( CI_STATE, field) + \
|
|
sizeof( state.field ) ) ) \
|
|
{ \
|
|
state.field = ( value ); \
|
|
}
|
|
|
|
|
|
SCODE CiCat::CiState( CI_STATE & state )
|
|
{
|
|
//
|
|
// NOTE - We are accessing _xCiManager without explicitly doing an
|
|
// AddRef() because this call is made very frequently. Making a
|
|
// Check about the "Started" state is sufficient - we are cheating
|
|
// but it is a safe cheat.
|
|
//
|
|
SCODE sc = S_OK;
|
|
|
|
if ( IsStarted() )
|
|
{
|
|
CIF_STATE cifState;
|
|
|
|
cifState.cbStruct = sizeof( CIF_STATE );
|
|
sc = _xCiManager->GetStatus( &cifState );
|
|
if ( S_OK == sc )
|
|
{
|
|
|
|
//
|
|
// Copy the status information from the CIF_STATE format to
|
|
// CI_STATE format.
|
|
//
|
|
state.cbStruct = min( state.cbStruct, sizeof(CI_STATE) );
|
|
|
|
setState( cWordList, cifState.cWordList );
|
|
setState( cPersistentIndex, cifState.cPersistentIndex );
|
|
setState( cQueries, cifState.cQueries );
|
|
setState( cDocuments, cifState.cDocuments );
|
|
setState( cSecQDocuments, cifState.cSecQDocuments );
|
|
setState( cFreshTest, cifState.cFreshTest );
|
|
setState( dwMergeProgress, cifState.dwMergeProgress );
|
|
setState( cFilteredDocuments, cifState.cFilteredDocuments );
|
|
setState( dwIndexSize, cifState.dwIndexSize );
|
|
setState( cUniqueKeys, cifState.cUniqueKeys );
|
|
setState( dwPropCacheSize, _propstoremgr.GetTotalSizeInKB() );
|
|
|
|
ULONG cTotalDocuments, cPendingScans, stateFlags;
|
|
CatalogState( cTotalDocuments, cPendingScans, stateFlags );
|
|
|
|
setState( cTotalDocuments, cTotalDocuments );
|
|
setState( cPendingScans, cPendingScans );
|
|
|
|
ULONG eCiState = stateFlags;
|
|
|
|
if ( ! IsReadOnly() )
|
|
{
|
|
if ( IsStarting() )
|
|
eCiState = CI_STATE_STARTING;
|
|
else
|
|
{
|
|
if ( cifState.eState & CIF_STATE_SHADOW_MERGE )
|
|
eCiState |= CI_STATE_SHADOW_MERGE;
|
|
|
|
if ( cifState.eState & CIF_STATE_MASTER_MERGE )
|
|
eCiState |= CI_STATE_MASTER_MERGE;
|
|
|
|
if ( cifState.eState & CIF_STATE_CONTENT_SCAN_REQUIRED )
|
|
eCiState |= CI_STATE_CONTENT_SCAN_REQUIRED;
|
|
|
|
if ( cifState.eState & CIF_STATE_ANNEALING_MERGE )
|
|
eCiState |= CI_STATE_ANNEALING_MERGE;
|
|
|
|
if ( cifState.eState & CIF_STATE_INDEX_MIGRATION_MERGE )
|
|
eCiState |= CI_STATE_INDEX_MIGRATION_MERGE;
|
|
|
|
if ( cifState.eState & CIF_STATE_MASTER_MERGE_PAUSED )
|
|
eCiState |= CI_STATE_MASTER_MERGE_PAUSED;
|
|
|
|
if ( cifState.eState & CIF_STATE_HIGH_IO )
|
|
eCiState |= CI_STATE_HIGH_IO;
|
|
|
|
if ( cifState.eState & CIF_STATE_LOW_MEMORY )
|
|
eCiState |= CI_STATE_LOW_MEMORY;
|
|
|
|
if ( cifState.eState & CIF_STATE_BATTERY_POWER )
|
|
eCiState |= CI_STATE_BATTERY_POWER;
|
|
|
|
if ( cifState.eState & CIF_STATE_USER_ACTIVE )
|
|
eCiState |= CI_STATE_USER_ACTIVE;
|
|
|
|
if ( !_usnMgr.IsWaitingForUpdates() )
|
|
eCiState |= CI_STATE_READING_USNS;
|
|
}
|
|
}
|
|
|
|
setState( eState, eCiState );
|
|
|
|
}
|
|
}
|
|
else if ( IsShuttingDown() )
|
|
sc = CI_E_SHUTDOWN;
|
|
else
|
|
sc = CI_E_NOT_INITIALIZED;
|
|
|
|
return sc;
|
|
} //CiState
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::InitIf
|
|
//
|
|
// Synopsis: Initializes CiCat if not already initialized.
|
|
//
|
|
// History: 2-27-96 srikants Created
|
|
//
|
|
// Notes: To reduce lock contention, check to see if we're already
|
|
// initialized without taking the lock.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::InitIf( BOOL fLeaveCorruptCatalog )
|
|
{
|
|
if ( IsInit() )
|
|
{
|
|
if ( !_statusMonitor.IsOk() )
|
|
THROW( CException( _statusMonitor.GetStatus() ) );
|
|
|
|
return;
|
|
}
|
|
|
|
CLock lock(_mutex);
|
|
CImpersonateSystem impersonate;
|
|
|
|
if ( !IsInit() && _statusMonitor.IsOk() && LokExists() )
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
TRY
|
|
{
|
|
LokInit();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
sc = e.GetErrorCode();
|
|
ciDebugOut(( DEB_ERROR, "Error 0x%X while initializing catalog (%ws) \n",
|
|
sc, _wcsCatDir ));
|
|
}
|
|
END_CATCH
|
|
|
|
if ( S_OK != sc )
|
|
{
|
|
//
|
|
// Normal version changes will be detected much earlier. A
|
|
// version change detected here is due to corruption.
|
|
//
|
|
|
|
if ( IsCiCorruptStatus(sc) || CI_INCORRECT_VERSION == sc )
|
|
{
|
|
if ( fLeaveCorruptCatalog )
|
|
{
|
|
Win4Assert( !"leaving corrupt catalog" );
|
|
THROW( CException( sc ) );
|
|
}
|
|
|
|
// Win4Assert( !"FSCI(Catalog) Data Corruption" );
|
|
|
|
//
|
|
// Log an event that we are doing automatic recovery.
|
|
//
|
|
|
|
_statusMonitor.LogEvent( CCiStatusMonitor::eCiRemoved );
|
|
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
Win4Assert( 0 != _pStorage );
|
|
_pStorage->DeleteAllFsCiFiles();
|
|
}
|
|
|
|
_statusMonitor.Reset();
|
|
LokInit();
|
|
sc = S_OK;
|
|
}
|
|
else
|
|
{
|
|
THROW( CException( sc ) );
|
|
}
|
|
}
|
|
|
|
Win4Assert( S_OK == sc );
|
|
|
|
_scanMgr.StartRecovery();
|
|
}
|
|
else if ( !_statusMonitor.IsOk() )
|
|
{
|
|
THROW( CException( _statusMonitor.GetStatus() ) );
|
|
}
|
|
} //InitIf
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CreateIf
|
|
//
|
|
// Synopsis: Creates CiCat if not already created.
|
|
//
|
|
// History: 2-27-96 srikants Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::CreateIf()
|
|
{
|
|
CLock lock(_mutex);
|
|
if ( !LokExists() )
|
|
LokCreate();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::InitOrCreate
|
|
//
|
|
// Synopsis: If there is no ContentIndex, one gets created and intialized
|
|
// If there is a ContentIndex but it is not initialized, it will
|
|
// be initialized.
|
|
//
|
|
// Otherwise, nothing is done.
|
|
//
|
|
// History: 1-21-96 srikants Moved from the UpdateDocuments method.
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::InitOrCreate()
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
|
|
CreateIf();
|
|
InitIf();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::UpdateDocuments, public
|
|
//
|
|
// Synopsis: Searches a directory and all its sub-directories for files
|
|
// to be updated.
|
|
//
|
|
// Arguments: [wcsRootPath] - directory to start search from
|
|
// [updFlag] - Flag indicating whether incremental or full
|
|
// update.
|
|
//
|
|
// History: 27-Mar-92 AmyA Created
|
|
// 16-Mar-98 KitmanH Don't rescan if the catalog is read-only
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::UpdateDocuments ( WCHAR const * wcsRootPath, ULONG updFlag )
|
|
{
|
|
if ( IsReadOnly() )
|
|
THROW( CException(STATUS_ACCESS_DENIED) );
|
|
|
|
Win4Assert( 0 != wcsRootPath );
|
|
|
|
InitOrCreate();
|
|
|
|
ciDebugOut (( DEB_ITRACE, "Updating the tree %ws\n", wcsRootPath ));
|
|
|
|
ScanOrAddScope( wcsRootPath, FALSE, updFlag, TRUE, TRUE ); // do deletions
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DoUpdate
|
|
//
|
|
// Synopsis: Synchronously scans the given scope and updates CI with
|
|
// paths from the given scope. It is assumed that the
|
|
// initialization is already done.
|
|
//
|
|
// Arguments: [pwcsPath] - Root of the scope to update from.
|
|
// [updFlag] - Update flag specifying either incremental
|
|
// or full.
|
|
// [fDoDeletions] - Should deletion be done ?
|
|
// [fAbort] - Set to TRUE to abort processing
|
|
// [fProcessRoot] - In the case of a directory, should the root
|
|
// be filtered ?
|
|
//
|
|
// History: 1-21-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::DoUpdate( WCHAR const * pwcPath,
|
|
ULONG updFlag,
|
|
BOOL fDoDeletions,
|
|
BOOL & fAbort,
|
|
BOOL fProcessRoot )
|
|
{
|
|
Win4Assert( 0 != pwcPath );
|
|
Win4Assert( UPD_INCREM == updFlag || UPD_FULL == updFlag );
|
|
|
|
CLowerFunnyPath lcaseFunnyRootPath;
|
|
lcaseFunnyRootPath.SetPath( pwcPath );
|
|
|
|
// make sure root is given a wid
|
|
// NOTE: this is also done inside the CUpdate ctor
|
|
|
|
WORKID widRoot = PathToWorkId( lcaseFunnyRootPath, TRUE );
|
|
|
|
CUpdate upd( *this, *(_xCiManager.GetPointer()),
|
|
lcaseFunnyRootPath, GetPartition(), updFlag == UPD_INCREM,
|
|
fDoDeletions, fAbort, fProcessRoot );
|
|
|
|
upd.EndProcessing();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DoUpdate
|
|
//
|
|
// Synopsis: Scans all the specified scopes for updates
|
|
//
|
|
// Arguments: [scopes] - Queue of scopes to scan
|
|
// [scanMgr] - a reference to the scan manager
|
|
// [fAbort] - a reference to a flag that will be set to TRUE
|
|
// if the scan should be aborted
|
|
//
|
|
// History: 1-23-96 srikants Created
|
|
//
|
|
// Notes: **** Does NOT THROW ****
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::DoUpdate( CScanInfoList & scopes,
|
|
CCiScanMgr & scanMgr,
|
|
BOOL & fAbort )
|
|
{
|
|
|
|
//
|
|
// Performance OPTIMIZATION
|
|
// Modify this later to scan the PropertyStore just once
|
|
// for all the paths in the stack instead of once per path.
|
|
//
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
for ( CFwdScanInfoIter scanInfoIter(scopes);
|
|
!scopes.AtEnd(scanInfoIter);
|
|
scopes.Advance(scanInfoIter) )
|
|
{
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"CiCat::DoUpdate - Shutdown initiated. Stopping scans\n" ));
|
|
break;
|
|
}
|
|
|
|
CCiScanInfo * pScanInfo = scanInfoIter.GetEntry();
|
|
Win4Assert( pScanInfo->GetRetries() <= CCiScanInfo::MAX_RETRIES );
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TRY
|
|
{
|
|
if ( scanMgr.IsScopeDeleted( pScanInfo ) )
|
|
{
|
|
RemovePathsFromCiCat( pScanInfo->GetPath(), eScansArray );
|
|
scanMgr.SetDone( pScanInfo );
|
|
}
|
|
else if ( scanMgr.IsRenameDir( pScanInfo ) )
|
|
{
|
|
CLowerFunnyPath lcaseFunnyOldDir, lcaseFunnyNewDir;
|
|
lcaseFunnyOldDir.SetPath( pScanInfo->GetDirOldName() );
|
|
lcaseFunnyNewDir.SetPath( pScanInfo->GetPath() );
|
|
|
|
CRenameDir renameDir( *this,
|
|
lcaseFunnyOldDir,
|
|
lcaseFunnyNewDir,
|
|
fAbort,
|
|
CI_VOLID_USN_NOT_ENABLED );
|
|
|
|
if ( fAbort )
|
|
break;
|
|
|
|
scanMgr.SetScanSuccess( pScanInfo );
|
|
}
|
|
else
|
|
{
|
|
DoUpdate( pScanInfo->GetPath(),
|
|
pScanInfo->GetFlags(),
|
|
pScanInfo->IsDoDeletions(),
|
|
fAbort,
|
|
pScanInfo->GetProcessRoot() );
|
|
if ( fAbort )
|
|
break;
|
|
|
|
scanMgr.SetScanSuccess( pScanInfo );
|
|
}
|
|
}
|
|
CATCH( CException, e)
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Exception 0x%x caught in Cicat::DoUpdate\n", e.GetErrorCode() ));
|
|
|
|
scanMgr.SetStatus( pScanInfo, e.GetErrorCode() );
|
|
status = e.GetErrorCode();
|
|
}
|
|
END_CATCH
|
|
|
|
if ( STATUS_SUCCESS != status )
|
|
{
|
|
HandleError( status );
|
|
if ( _statusMonitor.IsLowOnDisk() )
|
|
break;
|
|
}
|
|
}
|
|
} //DoUpdate
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ScanOrAddScope
|
|
//
|
|
// Synopsis: Adds the specified scope to the ContentIndex, if one is not
|
|
// already added.
|
|
//
|
|
// Arguments: [pwszScope] - Scope to be added to the ContentIndex
|
|
// [fAdd] - If set to TRUE, the scope will be added if
|
|
// not already present.
|
|
// [updFlag] - Flag indicating whether incremental or full
|
|
// update.
|
|
// [fDoDeletions] - Set to TRUE if paths in the catalog that were
|
|
// not found in the scope must be deleted from
|
|
// CI (garbage collection).
|
|
// [fScanIfNotNew] - If TRUE, the scope is scanned even if
|
|
// it was already in the scope table.
|
|
// [fCreateShadow] - TRUE if a shadow entry for this scope
|
|
// should be created in the registry.
|
|
//
|
|
// History: 1-21-96 srikants Created
|
|
// 9-16-98 kitmanh When a new scope is added on the fly,
|
|
// register the volume where the scope is
|
|
// for device notifications
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ScanOrAddScope( WCHAR const * pwszScope,
|
|
BOOL fAdd,
|
|
ULONG updFlag,
|
|
BOOL fDoDeletions,
|
|
BOOL fScanIfNotNew,
|
|
BOOL fCreateShadow )
|
|
{
|
|
Win4Assert( 0 != pwszScope );
|
|
|
|
InitOrCreate();
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
CLowerFunnyPath lcase( pwszScope );
|
|
if ( lcase.IsShortPath() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Converting %ws to long filename ",
|
|
lcase.GetActualPath() ));
|
|
|
|
if ( lcase.ConvertToLongName() )
|
|
{
|
|
ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n",
|
|
lcase.GetActualPath() ));
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Couldn't convert short filename %ws.\n",
|
|
lcase.GetActualPath() ));
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
}
|
|
if ( lcase.GetActualLength() >= MAX_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "ScanOrAddScope: Too big (%d) a path (%ws)\n",
|
|
lcase.GetActualLength(), lcase.GetActualPath() ));
|
|
CCiStatusMonitor::ReportPathTooLong( lcase.GetActualPath() );
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
|
|
lcase.AppendBackSlash( );
|
|
|
|
if ( !IsEligibleForFiltering( lcase ) )
|
|
return ;
|
|
|
|
|
|
_evtPh2Init.Wait();
|
|
if ( IsShuttingDown() )
|
|
return;
|
|
|
|
BOOL fSupportsUsns = VolumeSupportsUsns( lcase.GetActualPath()[0] );
|
|
|
|
VOLUMEID volumeId;
|
|
if ( fSupportsUsns )
|
|
volumeId = MapPathToVolumeId( lcase.GetActualPath() );
|
|
else
|
|
volumeId = CI_VOLID_USN_NOT_ENABLED;
|
|
|
|
BOOL fInScope = _notify.IsInScope( lcase.GetActualPath() );
|
|
if ( !fInScope && fAdd )
|
|
{
|
|
//
|
|
// Log event
|
|
//
|
|
|
|
CEventLog eventLog( NULL, wcsCiEventSource );
|
|
CEventItem item( EVENTLOG_INFORMATION_TYPE,
|
|
CI_SERVICE_CATEGORY,
|
|
MSG_CI_PROOT_ADDED,
|
|
1 );
|
|
|
|
item.AddArg( lcase.GetActualPath() );
|
|
|
|
eventLog.ReportEvent( item );
|
|
|
|
//
|
|
// Register the volume where the scope is for Device notifcations
|
|
//
|
|
ciDebugOut(( DEB_ITRACE, "Register volume %wc for device notifications\n",
|
|
pwszScope[0] ));
|
|
if ( L'\\' != toupper(pwszScope[0]) )
|
|
_DrvNotifArray.AddDriveNotification( pwszScope[0] );
|
|
|
|
//
|
|
// The given scope is not already in this ContentIndex. It must
|
|
// be added to CI.
|
|
//
|
|
{
|
|
CLock lock(_mutex);
|
|
// add the path to the persistent list of scopes.
|
|
_scopeTable.AddScope( volumeId,
|
|
lcase.GetActualPath(),
|
|
fCreateShadow ? _xScopesKey.Get() + SKIP_TO_LM: 0 );
|
|
}
|
|
|
|
BOOL fSubScopesRemoved = FALSE;
|
|
|
|
//
|
|
// Enable notifications on this scope. Usn scopes are also added to
|
|
// _notify, because the entries in _notify are serialized by the
|
|
// scope table, but the notifications are not turned on for usn
|
|
// scopes.
|
|
//
|
|
FILETIME ftLastScan;
|
|
RtlZeroMemory( &ftLastScan, sizeof(FILETIME) );
|
|
|
|
XPtr<CScopeInfo> xScopeInfo;
|
|
|
|
if ( fSupportsUsns )
|
|
xScopeInfo.Set( new CScopeInfo( lcase.GetActualPath(),
|
|
GetVolumeCreationTime( lcase.GetActualPath() ),
|
|
GetVolumeSerialNumber( lcase.GetActualPath() ),
|
|
volumeId,
|
|
0,
|
|
GetJournalId( lcase.GetActualPath() ),
|
|
TRUE ) );
|
|
else
|
|
xScopeInfo.Set( new CScopeInfo( lcase.GetActualPath(),
|
|
GetVolumeCreationTime( lcase.GetActualPath() ),
|
|
GetVolumeSerialNumber( lcase.GetActualPath() ),
|
|
ftLastScan ) );
|
|
|
|
_notify.AddPath( xScopeInfo.GetReference(), fSubScopesRemoved );
|
|
|
|
if ( fSubScopesRemoved )
|
|
{
|
|
//
|
|
// The scope table has some redundant paths (eg f:\foo, f:\bar)
|
|
// and now f:\ has been added. We must get rid of f:\foo and f:\bar
|
|
//
|
|
CLock lock2( _mutex );
|
|
_scopeTable.RemoveSubScopes( lcase.GetActualPath(), _xScopesKey.Get() + SKIP_TO_LM );
|
|
}
|
|
|
|
if ( fSupportsUsns )
|
|
{
|
|
//
|
|
// Inform usn manager to do a scan and start monitoring for
|
|
// notifications on this scope
|
|
//
|
|
|
|
USN usnStart = 0;
|
|
_usnMgr.AddScope( lcase.GetActualPath(),
|
|
volumeId,
|
|
FALSE, // No need for deletions for new scopes
|
|
usnStart,
|
|
FALSE, // not a full scan
|
|
FALSE, // not user-initiated
|
|
TRUE ); // new scope
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// trigger a background scan on the scope
|
|
//
|
|
_scanMgr.ScanScope( lcase.GetActualPath(),
|
|
GetPartition(),
|
|
UPD_FULL,
|
|
FALSE, // don't do deletions
|
|
FALSE, // not a delayed scan - immediate
|
|
TRUE ); // new scope
|
|
}
|
|
}
|
|
else if ( fInScope && fScanIfNotNew )
|
|
{
|
|
//
|
|
// Just force a re-scan with either usn manager or scan manager
|
|
//
|
|
|
|
if ( fSupportsUsns )
|
|
{
|
|
USN usnStart = 0;
|
|
BOOL fFull = ( updFlag == UPD_FULL );
|
|
|
|
InitUsnTreeScan( lcase.GetActualPath() );
|
|
_usnMgr.AddScope( lcase.GetActualPath(),
|
|
volumeId,
|
|
fDoDeletions,
|
|
usnStart,
|
|
FALSE, // not a full USN scan (which deletes all files first)
|
|
fFull ); // user-initiated if a full scan
|
|
}
|
|
else
|
|
{
|
|
_scanMgr.ScanScope( lcase.GetActualPath(),
|
|
GetPartition(),
|
|
updFlag,
|
|
fDoDeletions,
|
|
FALSE ); // not a delayed scan - immediate scan
|
|
}
|
|
}
|
|
} //ScanOrAddScope
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsScopeInCI
|
|
//
|
|
// Arguments: [pwszScope] - Scope to be checked
|
|
//
|
|
// Returns: TRUE if [pwszScope] has already been added to content index.
|
|
//
|
|
// History: 2-19-96 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsScopeInCI( WCHAR const * pwszScope )
|
|
{
|
|
Win4Assert( 0 != pwszScope );
|
|
|
|
InitOrCreate();
|
|
|
|
_evtPh2Init.Wait();
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "IsScopeInCI - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
//
|
|
// Terminate the path with a trailing backslash if not already terminated
|
|
// with a backslash.
|
|
//
|
|
|
|
CLowerFunnyPath lcase( pwszScope );
|
|
if ( lcase.IsShortPath() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Converting %ws to long filename ",
|
|
lcase.GetActualPath() ));
|
|
|
|
if ( lcase.ConvertToLongName() )
|
|
{
|
|
ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n",
|
|
lcase.GetActualPath() ));
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Couldn't convert short filename %ws.\n",
|
|
lcase.GetActualPath() ));
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
}
|
|
if ( lcase.GetActualLength() >= MAX_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n",
|
|
lcase.GetActualLength(), lcase.GetActualPath() ));
|
|
CCiStatusMonitor::ReportPathTooLong( lcase.GetActualPath() );
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
|
|
lcase.AppendBackSlash( );
|
|
|
|
return _notify.IsInScope( lcase.GetActualPath() );
|
|
} //IsScopeInCI
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RemoveScopeFromCI
|
|
//
|
|
// Synopsis: Removes scope permanently from ContentIndex
|
|
//
|
|
// Arguments: [wcsScopeToRemove] - the scope to remove
|
|
// [fForceRemovalScan] - if true, the scope is scanned and files
|
|
// in the scope are removed from the ci
|
|
//
|
|
// History: 1-25-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RemoveScopeFromCI( WCHAR const * wcsScopeToRemove,
|
|
BOOL fForceRemovalScan )
|
|
{
|
|
CScopeEntry scopeToRemove( wcsScopeToRemove );
|
|
|
|
InitOrCreate();
|
|
|
|
_evtPh2Init.Wait();
|
|
if ( IsShuttingDown() )
|
|
return;
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
//
|
|
// If scope to remove contains special chars, find which scopes need to be
|
|
// rescanned.
|
|
//
|
|
|
|
if ( !scopeToRemove.ContainsSpecialChar() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "RemoveThisScope(%ws)\n", scopeToRemove.Get() ));
|
|
|
|
RemoveThisScope( scopeToRemove.Get(), fForceRemovalScan );
|
|
}
|
|
else
|
|
{
|
|
if ( !RemoveMatchingScopeTableEntries( wcsScopeToRemove ) )
|
|
{
|
|
// nothing matched, must be file pattern or subdir
|
|
|
|
while ( _scopesIgnored.RegExFind( scopeToRemove.Get() ) )
|
|
{
|
|
|
|
if ( !scopeToRemove.SetToParentDirectory() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Reached Root: %ws, Rescanning \n", scopeToRemove.Get() ));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE, "ScanScopeTableEntry(%ws)\n",scopeToRemove.Get() ));
|
|
|
|
ScanScopeTableEntry( scopeToRemove.Get() );
|
|
}
|
|
}
|
|
} //RemoveScopeFromCI
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RemoveMatchingScopeTableEntries, private
|
|
//
|
|
// Synopsis: remove any scope table entries that matches input arg.
|
|
//
|
|
// Arguments: [pwszRegXScope] -- input scope to match against
|
|
//
|
|
// returns: TRUE if scope table entries & removed, FALSE otherwise.
|
|
//
|
|
// History: 4-12-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::RemoveMatchingScopeTableEntries( WCHAR const * pwszRegXScope )
|
|
{
|
|
CTimeLimit tl(0,0);
|
|
CDFA cdfa( pwszRegXScope, tl, FALSE );
|
|
BOOL bRetVal = FALSE;
|
|
|
|
unsigned iBmk = 0;
|
|
|
|
WCHAR awcRoot[MAX_PATH+1];
|
|
|
|
while ( !IsShuttingDown() &&
|
|
_scopeTable.Enumerate( awcRoot,
|
|
sizeof awcRoot / sizeof WCHAR,
|
|
iBmk ) )
|
|
{
|
|
if ( cdfa.Recognize(awcRoot) )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "RemoveThisScope(%ws)\n", awcRoot ));
|
|
|
|
RemoveThisScope( awcRoot, TRUE );
|
|
|
|
bRetVal = TRUE;
|
|
}
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RemoveThisScope
|
|
//
|
|
// Synopsis: Removes scope permanently from ContentIndex
|
|
//
|
|
// Arguments: [wcsPath] - the scope to remove
|
|
// [fForceRemovalScan] - if true, the scope is scanned and files
|
|
// in the scope are removed from the ci
|
|
//
|
|
// History: 1-25-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RemoveThisScope(WCHAR const * wcsPath, BOOL fForceRemovalScan )
|
|
{
|
|
CLowerFunnyPath lcase( wcsPath );
|
|
if ( lcase.IsShortPath() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Converting %ws to long filename ",
|
|
lcase.GetActualPath() ));
|
|
|
|
if ( lcase.ConvertToLongName() )
|
|
{
|
|
ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n",
|
|
lcase.GetActualPath() ));
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Couldn't convert short filename %ws.\n",
|
|
lcase.GetActualPath() ));
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log event
|
|
//
|
|
|
|
CEventLog eventLog( NULL, wcsCiEventSource );
|
|
CEventItem item( EVENTLOG_INFORMATION_TYPE,
|
|
CI_SERVICE_CATEGORY,
|
|
MSG_CI_PROOT_REMOVED,
|
|
1 );
|
|
|
|
item.AddArg( lcase.GetActualPath() );
|
|
|
|
eventLog.ReportEvent( item );
|
|
|
|
// =================================================
|
|
{
|
|
CLock lock(_mutex);
|
|
_scopeTable.RemoveScope( lcase.GetActualPath(), _xScopesKey.Get() + SKIP_TO_LM );
|
|
}
|
|
// =================================================
|
|
|
|
//
|
|
// Remove the scope from the notification manager to prevent further
|
|
// scan triggers.
|
|
//
|
|
BOOL fInCatalog = TRUE;
|
|
|
|
if ( !_notify.RemoveScope( lcase.GetActualPath() ) )
|
|
{
|
|
fInCatalog = FALSE;
|
|
ciDebugOut(( DEB_ERROR, "Path (%ws) not in catalog. Not deleted\n",
|
|
lcase.GetActualPath() ));
|
|
}
|
|
|
|
// Inform the scan manager to stop any in-progress scans on this
|
|
// scope, and remove any files in this scope from the catalog
|
|
|
|
if ( fInCatalog || fForceRemovalScan )
|
|
{
|
|
BOOL fSupportsUsns = VolumeSupportsUsns( lcase.GetActualPath()[0] );
|
|
if ( fSupportsUsns )
|
|
{
|
|
//
|
|
// Remove scope from usn manager
|
|
//
|
|
VOLUMEID volumeId = MapPathToVolumeId( lcase.GetActualPath() );
|
|
_usnMgr.RemoveScope( lcase.GetActualPath(), volumeId );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Remove scope from scan manager
|
|
//
|
|
_scanMgr.RemoveScope( lcase.GetActualPath() );
|
|
}
|
|
}
|
|
} //RemoveThisScope
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AddVirtualScope, public
|
|
//
|
|
// Synopsis: Add virtual/physical mapping to index
|
|
//
|
|
// Arguments: [vroot] -- Virtual root
|
|
// [root] -- Physical root
|
|
// [fAutomatic] -- TRUE for paths tracking Gibraltar
|
|
// [eType] -- Type of vroot
|
|
// [fVRoot] -- TRUE if a virtual root, not just a vpath
|
|
// [fIsIndexed] -- TRUE if indexed, FALSE otherwise
|
|
//
|
|
// Returns: TRUE if a change was made.
|
|
//
|
|
// History: 2-05-96 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::AddVirtualScope(
|
|
WCHAR const * vroot,
|
|
WCHAR const * root,
|
|
BOOL fAutomatic,
|
|
CiVRootTypeEnum eType,
|
|
BOOL fVRoot,
|
|
BOOL fIsIndexed )
|
|
{
|
|
CLowcaseBuf lcaseVRoot( vroot );
|
|
CLowerFunnyPath lcaseRoot( root );
|
|
|
|
if ( lcaseRoot.IsShortPath() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "Converting %ws to long filename ",
|
|
lcaseRoot.GetActualPath() ));
|
|
|
|
if ( lcaseRoot.ConvertToLongName() )
|
|
{
|
|
ciDebugOut(( DEB_WARN | DEB_NOCOMPNAME, "\t%ws\n",
|
|
lcaseRoot.GetActualPath() ));
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"Couldn't convert short filename %ws.\n",
|
|
lcaseRoot.GetActualPath() ));
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
}
|
|
|
|
if ( lcaseRoot.GetActualLength() >= MAX_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n",
|
|
lcaseRoot.GetActualLength(), lcaseRoot.GetActualPath() ));
|
|
CCiStatusMonitor::ReportPathTooLong( root );
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
|
|
if ( lcaseVRoot.Length() >= MAX_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n",
|
|
lcaseVRoot.Length(), lcaseVRoot.Get() ));
|
|
CCiStatusMonitor::ReportPathTooLong( vroot );
|
|
THROW( CException(STATUS_INVALID_PARAMETER) );
|
|
}
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
_evtPh2Init.Wait();
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "AddVirtualScope - Shutdown Initiated\n" ));
|
|
THROW( CException(STATUS_TOO_LATE) );
|
|
}
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
BOOL fAdded;
|
|
|
|
{
|
|
CLock lock ( _mutex );
|
|
fAdded = _strings.AddVirtualScope( lcaseVRoot.Get(),
|
|
lcaseRoot.GetActualPath(),
|
|
fAutomatic,
|
|
eType,
|
|
fVRoot,
|
|
fIsIndexed );
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Virtual scope %.*ws %s list\n",
|
|
lcaseVRoot.Length(), lcaseVRoot.Get(),
|
|
fAdded ? "added to" : "already in" ));
|
|
|
|
//
|
|
// If the root was added to the vmap, add it to the scope table
|
|
//
|
|
|
|
if ( fVRoot)
|
|
{
|
|
if ( fIsIndexed &&
|
|
( ( _fIndexW3Roots && ( W3VRoot == eType ) ) ||
|
|
( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) ||
|
|
( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ) &&
|
|
!IsScopeInCI( root ) )
|
|
{
|
|
AddScopeToCI( root, TRUE );
|
|
ciDebugOut(( DEB_ITRACE, "Physical scope %ws added to list\n", root ));
|
|
}
|
|
|
|
if ( !fIsIndexed )
|
|
RemoveVirtualScope( lcaseVRoot.Get(), FALSE, eType, fVRoot, fAdded );
|
|
}
|
|
|
|
return fAdded;
|
|
} //AddVirtualScope
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: AreVRootTypesEquiv
|
|
//
|
|
// Synopsis: Returns TRUE if the two enums are equivalent
|
|
//
|
|
// Arguments: [eType] -- Virtual root
|
|
// [rootType] -- The other style of enum
|
|
//
|
|
// Returns: TRUE if they are the same, FALSE otherwise
|
|
//
|
|
// History: 6-20-97 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL AreVRootTypesEquiv(
|
|
CiVRootTypeEnum eType,
|
|
ULONG rootType )
|
|
{
|
|
if ( NNTPVRoot == eType && ( PCatalog::NNTPRoot == rootType ) )
|
|
return TRUE;
|
|
|
|
if ( IMAPVRoot == eType && ( PCatalog::IMAPRoot == rootType ) )
|
|
return TRUE;
|
|
|
|
if ( W3VRoot == eType && ( 0 == rootType ) )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RemoveVirtualScope, public
|
|
//
|
|
// Synopsis: Remove virtual/physical mapping to index
|
|
//
|
|
// Arguments: [vroot] -- Virtual root
|
|
// [fOnlyIfAutomatic] -- If TRUE, then a manual root will not
|
|
// be removed
|
|
// [eType] -- Type of vroot
|
|
// [fVRoot] -- TRUE if a vroot, false if a vdir
|
|
// [fForceVPathFixing] -- if TRUE, vpaths are fixed
|
|
//
|
|
// History: 2-05-96 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::RemoveVirtualScope( WCHAR const * vroot,
|
|
BOOL fOnlyIfAutomatic,
|
|
CiVRootTypeEnum eType,
|
|
BOOL fVRoot,
|
|
BOOL fForceVPathFixing )
|
|
{
|
|
CLowcaseBuf lcaseVRoot( vroot );
|
|
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
_evtPh2Init.Wait();
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "RemoveVirtualScope - Shutdown Initiated\n" ));
|
|
THROW( CException(STATUS_TOO_LATE) );
|
|
}
|
|
|
|
if ( lcaseVRoot.Length() >= MAX_PATH )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Too big (%d) a path (%ws)\n",
|
|
lcaseVRoot.Length(), lcaseVRoot.Get() ));
|
|
CCiStatusMonitor::ReportPathTooLong( vroot );
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
}
|
|
|
|
CLock lockAdmin( _mtxAdmin );
|
|
|
|
BOOL fRemoved; // TRUE if virtual root was removed
|
|
BOOL fDelete = FALSE; // TRUE if physic root should be deleted
|
|
BOOL fChildrenToBeAdded = FALSE; // TRUE if deletion of super-scope requires
|
|
// addition of sub-scopes.
|
|
CLowerFunnyPath lcaseFunnyPRoot;
|
|
unsigned cwcPRoot = 0;
|
|
|
|
{
|
|
CLock lock ( _mutex );
|
|
|
|
//
|
|
// Remember the physical root of the virtual root we're
|
|
// about to delete.
|
|
//
|
|
|
|
if ( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) ||
|
|
( _fIndexW3Roots && ( W3VRoot == eType ) ) ||
|
|
( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) )
|
|
{
|
|
_strings.VirtualToPhysicalRoot( lcaseVRoot.Get(),
|
|
lcaseVRoot.Length(),
|
|
lcaseFunnyPRoot,
|
|
cwcPRoot );
|
|
}
|
|
|
|
fRemoved = _strings.RemoveVirtualScope( lcaseVRoot.Get(),
|
|
fOnlyIfAutomatic,
|
|
eType,
|
|
fVRoot,
|
|
fForceVPathFixing );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Virtual scope %.*ws %s list\n",
|
|
lcaseVRoot.Length(), lcaseVRoot.Get(),
|
|
fRemoved ? "removed from" : "not in" ));
|
|
|
|
// no need to add/remove scopes from the CI if not a vroot
|
|
|
|
if ( !fVRoot )
|
|
return fRemoved;
|
|
|
|
//
|
|
// If we're running in 'automatic' mode, then we will remove the physical root
|
|
// when the last virtual reference is removed.
|
|
//
|
|
|
|
if ( fRemoved &&
|
|
( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) ||
|
|
( _fIndexW3Roots && ( W3VRoot == eType ) ) ||
|
|
( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ) )
|
|
{
|
|
fDelete = TRUE;
|
|
BOOL fDone = FALSE;
|
|
|
|
unsigned iBmk = 0;
|
|
|
|
while ( !fDone && fDelete )
|
|
{
|
|
XGrowable<WCHAR> xwcsVRoot;
|
|
unsigned cwcVRoot;
|
|
|
|
CLowerFunnyPath lcaseFunnyPRoot2;
|
|
unsigned cwcPRoot2;
|
|
|
|
ULONG Type = _strings.EnumerateVRoot( xwcsVRoot,
|
|
cwcVRoot,
|
|
lcaseFunnyPRoot2,
|
|
cwcPRoot2,
|
|
iBmk );
|
|
|
|
if ( Type == PCatalog::EndRoot )
|
|
fDone = TRUE;
|
|
else if ( Type & PCatalog::UsedRoot )
|
|
{
|
|
if ( cwcPRoot2 == cwcPRoot &&
|
|
RtlEqualMemory( lcaseFunnyPRoot.GetActualPath(), lcaseFunnyPRoot2.GetActualPath(),
|
|
cwcPRoot * sizeof(WCHAR) ) )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Keeping physical root %.*ws\n",
|
|
cwcPRoot, lcaseFunnyPRoot.GetActualPath() ));
|
|
fDelete = FALSE;
|
|
}
|
|
else if ( cwcPRoot2 > cwcPRoot &&
|
|
(lcaseFunnyPRoot2.GetActualPath())[cwcPRoot] == L'\\' &&
|
|
RtlEqualMemory( lcaseFunnyPRoot.GetActualPath(), lcaseFunnyPRoot2.GetActualPath(),
|
|
cwcPRoot * sizeof(WCHAR) ) )
|
|
{
|
|
fChildrenToBeAdded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do the dirty deed...
|
|
//
|
|
|
|
if ( fDelete )
|
|
{
|
|
Win4Assert(( ( _fIndexNNTPRoots && ( NNTPVRoot == eType ) ) ||
|
|
( _fIndexW3Roots && ( W3VRoot == eType ) ) ||
|
|
( _fIndexIMAPRoots && ( IMAPVRoot == eType ) ) ));
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Deleting physical root %ws\n", lcaseFunnyPRoot.GetActualPath() ));
|
|
RemoveScopeFromCI( lcaseFunnyPRoot.GetActualPath(), FALSE );
|
|
|
|
if ( fChildrenToBeAdded )
|
|
{
|
|
BOOL fDone = FALSE;
|
|
|
|
unsigned iBmk = 0;
|
|
|
|
while ( !fDone )
|
|
{
|
|
XGrowable<WCHAR> xwcsVRoot;
|
|
unsigned cwcVRoot;
|
|
|
|
ULONG Type = EnumerateVRoot( xwcsVRoot,
|
|
cwcVRoot,
|
|
lcaseFunnyPRoot,
|
|
cwcPRoot,
|
|
iBmk );
|
|
|
|
if ( Type == PCatalog::EndRoot )
|
|
fDone = TRUE;
|
|
else if ( ( Type & PCatalog::UsedRoot ) &&
|
|
( AreVRootTypesEquiv( eType, Type ) ) )
|
|
{
|
|
if ( !IsScopeInCI( lcaseFunnyPRoot.GetActualPath() ) )
|
|
{
|
|
AddScopeToCI( lcaseFunnyPRoot.GetActualPath(), TRUE );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Physical sub-scope %ws added to list\n", lcaseFunnyPRoot.GetActualPath() ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// It is possible (though unlikely) that we accidentally deleted a scope
|
|
// that was both a physical and virtual root. Re-sync physical roots to
|
|
// re-add the physical root.
|
|
//
|
|
|
|
SynchWithRegistryScopes( FALSE );
|
|
}
|
|
|
|
return fRemoved;
|
|
} //RemoveVirtualScope
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::WorkIdToVirtualPath, public
|
|
//
|
|
// Synopsis: Returns a virtual path for a document given its work id.
|
|
//
|
|
// Arguments: [wid] -- The work id of the document
|
|
// [cSkip] -- Number of matching virtual roots to skip.
|
|
// [pwcBuf] -- buffer to copy the path
|
|
// [cwc] -- size of buffer [in/out]
|
|
//
|
|
// History: 07-Feb-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
unsigned CiCat::WorkIdToVirtualPath( WORKID wid,
|
|
unsigned cSkip,
|
|
XGrowable<WCHAR> & xBuf )
|
|
{
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToVirtualPath - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
Win4Assert( wid != widInvalid );
|
|
|
|
unsigned cwc ;
|
|
|
|
if ( !IsStarted() )
|
|
cwc = 0;
|
|
else
|
|
cwc = _strings.FindVirtual( wid, cSkip, xBuf );
|
|
|
|
return cwc;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::WorkIdToVirtualPath, public
|
|
//
|
|
// Synopsis: Returns a virtual path for a document given its work id.
|
|
//
|
|
// Arguments: [propRec] -- The property record to use
|
|
// [cSkip] -- Number of matching virtual roots to skip.
|
|
// [pwcBuf] -- buffer to copy the path
|
|
// [cwc] -- size of buffer [in/out]
|
|
//
|
|
// History: 07-Feb-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
unsigned CiCat::WorkIdToVirtualPath( CCompositePropRecord & propRec,
|
|
unsigned cSkip,
|
|
XGrowable<WCHAR> & xBuf )
|
|
{
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::WorkIdToVirtualPath - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
unsigned cwc;
|
|
|
|
if ( !IsStarted() )
|
|
cwc = 0;
|
|
else
|
|
cwc = _strings.FindVirtual( propRec, cSkip, xBuf );
|
|
|
|
return cwc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::VirtualToPhysicalRoot, public
|
|
//
|
|
// Synopsis: Given a virtual path, returns a virtual root under that
|
|
// virtual path, and the corresponding physical root. Will
|
|
// not return overlapping virtual roots.
|
|
//
|
|
// Arguments: [pwcVPath] -- Virtual path
|
|
// [ccVPath] -- Size in chars of [pwcVPath]
|
|
// [xwcsVRoot] -- Virtual root
|
|
// [ccVRoot] -- Returns count of chars in [xwcsVRoot]
|
|
// [lcaseFunnyPRoot] -- Physical root
|
|
// [ccPRoot] -- Returns count of actual chars (excluding
|
|
// funny path) in [lcaseFunnyPRoot]
|
|
// [iBmk] -- Bookmark for iteration.
|
|
//
|
|
// Returns: TRUE if match was found.
|
|
//
|
|
// History: 11-Feb-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::VirtualToPhysicalRoot( WCHAR const * pwcVPath,
|
|
unsigned ccVPath,
|
|
XGrowable<WCHAR> & xwcsVRoot,
|
|
unsigned & ccVRoot,
|
|
CLowerFunnyPath & lcaseFunnyPRoot,
|
|
unsigned & ccPRoot,
|
|
unsigned & iBmk )
|
|
{
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
return _strings.VirtualToPhysicalRoot( pwcVPath,
|
|
ccVPath,
|
|
xwcsVRoot,
|
|
ccVRoot,
|
|
lcaseFunnyPRoot,
|
|
ccPRoot,
|
|
iBmk );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EnumerateVRoot, public
|
|
//
|
|
// Synopsis: Iterates all virtual roots.
|
|
//
|
|
// Arguments: [xwcVRoot] -- Virtual root
|
|
// [ccVRoot] -- Returns count of chars in [xwcVRoot]
|
|
// [lcaseFunnyPRoot] -- Physical root
|
|
// [ccPRoot] -- Returns count of actual chars (excluding
|
|
// funny path) in [lcaseFunnyPRoot]
|
|
// [iBmk] -- Bookmark for iteration.
|
|
//
|
|
// Returns: Type of root. PCatalog::EndRoot at end of iteration
|
|
//
|
|
// History: 11-Feb-96 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
ULONG CiCat::EnumerateVRoot( XGrowable<WCHAR> & xwcVRoot,
|
|
unsigned & ccVRoot,
|
|
CLowerFunnyPath & lcaseFunnyPRoot,
|
|
unsigned & ccPRoot,
|
|
unsigned & iBmk )
|
|
{
|
|
if ( !IsStarted() )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
if ( IsShutdown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "CiCat::EnumerateVRoot - Shutdown Initiated\n" ));
|
|
THROW( CException( STATUS_TOO_LATE ) );
|
|
}
|
|
|
|
return _strings.EnumerateVRoot( xwcVRoot,
|
|
ccVRoot,
|
|
lcaseFunnyPRoot,
|
|
ccPRoot,
|
|
iBmk );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RemovePathsFromCiCat
|
|
//
|
|
// Synopsis: Removes all the paths below the specified root from the
|
|
// property store. This method must be called ONLY from the
|
|
// scan thread.
|
|
//
|
|
// Arguments: [pwszRoot] - Root of the scope to remove paths from.
|
|
// [eType] - Seen array type
|
|
//
|
|
// History: 1-26-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RemovePathsFromCiCat( WCHAR const * pwszRoot, ESeenArrayType eType )
|
|
{
|
|
// NTRAID#DB-NTBUG9-83756-2000/07/31-dlee removing paths from cicat isn't restartable on crashes
|
|
|
|
//
|
|
// Remove it from the hash table and the propertyStore.
|
|
//
|
|
|
|
_strings.BeginSeen( pwszRoot, _mutex, eType );
|
|
EndUpdate( TRUE, eType ); // delete the paths.
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SerializeChangesInfo
|
|
//
|
|
// Synopsis: Serializes last scan time and usn flushed info
|
|
//
|
|
// History: 20-Aug-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::SerializeChangesInfo()
|
|
{
|
|
{
|
|
CLock lock( _mutex );
|
|
_notify.UpdateLastScanTimes( _ftLastCLFlush, _usnFlushInfoList );
|
|
}
|
|
|
|
//
|
|
// Flush the scope table which will persist the last scan time and usn
|
|
// flush info
|
|
//
|
|
|
|
_scopeTable.ProcessChangesFlush();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ProcessScansComplete
|
|
//
|
|
// Synopsis: Called when there are no more scans outstanding to update
|
|
// any persistent state information related to scans.
|
|
//
|
|
// Arguments: [fForce] - Set to TRUE if scope table must be updated
|
|
// with the scan time even if there were no updates.
|
|
// This is set when a "force scan" is completed.
|
|
// [fShortWait] - [output] Set to TRUE if the scan thread must
|
|
// do a wait for a shorter time than the usual 30 minutes.
|
|
// This is set to TRUE when there is a low resource
|
|
// situation and the scan thread must wake up
|
|
// frequently to check if the situation has improved.
|
|
// This is also set to TRUE when the initialization
|
|
// is not done yet.
|
|
//
|
|
// History: 1-26-96 srikants Created
|
|
// 1-27-97 srikants Added the fShortWait parameter
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ProcessScansComplete( BOOL fForce, BOOL & fShortWait )
|
|
{
|
|
fShortWait = TRUE;
|
|
|
|
if ( IsShuttingDown() || !_fInitialized )
|
|
return;
|
|
|
|
SerializeChangesInfo();
|
|
|
|
//
|
|
// Always flush the strings table and property store.
|
|
// Property store may get updated without additional documents because
|
|
// of filtering.
|
|
//
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
_fileIdMap.LokFlush();
|
|
_strings.LokFlush();
|
|
}
|
|
|
|
_propstoremgr.Flush();
|
|
|
|
_notify.ResetUpdateCount();
|
|
|
|
//
|
|
// Book-keeping - Schedule any scans if necessary.
|
|
//
|
|
_scopeTable.ScheduleScansIfNeeded( _docStore );
|
|
|
|
//
|
|
// See if any scans must be done on network paths with no notifications.
|
|
//
|
|
_notify.ForceNetPathScansIf();
|
|
|
|
// NTRAID#DB-NTBUG9-83758-2000/07/31-dlee If failed scans, don't record scans complete
|
|
|
|
_scopeTable.RecordScansComplete();
|
|
|
|
fShortWait = _scopeTable.IsFullScanNeeded() ||
|
|
_scopeTable.IsIncrScanNeeded() ||
|
|
_statusMonitor.IsLowOnDisk();
|
|
|
|
// Scans are complete and we won't need that code for awhile
|
|
|
|
if ( _regParams.GetMinimizeWorkingSet() )
|
|
SetProcessWorkingSetSize( GetCurrentProcess(),
|
|
(SIZE_T) -1,
|
|
(SIZE_T) -1 );
|
|
} //ProcessScansComplete
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ReScanPath
|
|
//
|
|
// Synopsis: Rescans the given scope. It is called when a notification
|
|
// is lost for a scope.
|
|
//
|
|
// Arguments: [wcsPath] - The scope to be rescanned. It is assumed that
|
|
// this path is already of interest to CI and so no checks
|
|
// are made to verify that this is a path in CI.
|
|
//
|
|
// History: 1-22-96 srikants Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ReScanPath( WCHAR const * wcsPath, BOOL fDelayed )
|
|
{
|
|
_scanMgr.ScanScope( wcsPath, GetPartition(), UPD_INCREM,
|
|
TRUE, // do deletions
|
|
fDelayed // delayed scan
|
|
);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::StartUpdate, public
|
|
//
|
|
// Synopsis: Marks start of update
|
|
//
|
|
// Arguments: [time] - (out) the time of the last update of the catalog
|
|
// [pwcsRoot] - Root
|
|
// [fDoDeletions] - Should wids be marked as seen/unseen ?
|
|
// [eType] - Seen array type
|
|
//
|
|
// History: 10-Mar-92 BartoszM Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::StartUpdate ( FILETIME* time,
|
|
WCHAR const * pwcsRoot,
|
|
BOOL fDoDeletions,
|
|
ESeenArrayType eType )
|
|
{
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"Shutting down in the middle of StartUpdate\n" ));
|
|
THROW( CException(STATUS_TOO_LATE) );
|
|
}
|
|
|
|
Win4Assert( IsStarted() );
|
|
|
|
if ( fDoDeletions )
|
|
_strings.BeginSeen( pwcsRoot, _mutex, eType );
|
|
|
|
_notify.GetLastScanTime( pwcsRoot, *time );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::EndUpdate, public
|
|
//
|
|
// Synopsis: Marks the end of the update
|
|
//
|
|
// Arguments: [fDoDeletions] - if TRUE, unseen files will be deleted and
|
|
// files not seen before will be added.
|
|
// [eType] - Seen array type
|
|
//
|
|
// History: 10-Mar-92 BartoszM Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::EndUpdate ( BOOL fDoDeletions, ESeenArrayType eType )
|
|
{
|
|
USN usn = 0;
|
|
|
|
if ( !fDoDeletions )
|
|
return;
|
|
|
|
ciDebugOut (( DEB_CAT, "Files not seen:\n" ));
|
|
|
|
int cDeletedTotal = 0;
|
|
int cNewTotal = 0;
|
|
const PARTITIONID partId = GetPartition();
|
|
|
|
CPropertyStoreWids iter( _propstoremgr );
|
|
|
|
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() )
|
|
{
|
|
if ( IsShuttingDown() )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Shutting down in the middle of EndUpdate\n" ));
|
|
THROW( CException(STATUS_TOO_LATE) );
|
|
}
|
|
|
|
// ==================== lock ===================
|
|
CLock lock( _mutex );
|
|
|
|
if ( _strings.LokNotSeen( wid, eType ) )
|
|
{
|
|
# if CIDBG == 1
|
|
XGrowable<WCHAR> awc;
|
|
_strings.Find( wid, awc );
|
|
ciDebugOut (( DEB_CAT, "\tdel: %u (0x%x) %ws\n", wid, wid, awc.Get() ));
|
|
# endif
|
|
|
|
// Files from USN volumes can only be deleted if the scan was
|
|
// from a USN volume -- only do this work if appropriate.
|
|
|
|
if ( eUsnsArray == eType )
|
|
{
|
|
//
|
|
// If the wid is from an ntfs 5.0 volume, then remove it
|
|
// from the file id map.
|
|
//
|
|
|
|
PROPVARIANT propVar;
|
|
BOOL fFound = _propstoremgr.ReadPrimaryProperty( wid,
|
|
pidFileIndex,
|
|
propVar );
|
|
FILEID fileId = propVar.uhVal.QuadPart;
|
|
|
|
if ( fileIdInvalid != fileId )
|
|
{
|
|
//
|
|
// File can be added to propstore, then deleted before
|
|
// the file can be filtered, which means that it's
|
|
// possible for a file to be in propstore but not in
|
|
// fileid map.
|
|
//
|
|
|
|
fFound = _propstoremgr.ReadPrimaryProperty( wid,
|
|
pidVolumeId,
|
|
propVar );
|
|
|
|
Win4Assert( fFound );
|
|
|
|
WORKID widExisting = _fileIdMap.LokFind( fileId, propVar.ulVal );
|
|
if ( widExisting != widInvalid && wid == widExisting )
|
|
{
|
|
//
|
|
// If wid != widExisting then it means that
|
|
// LokWriteFileUsnInfo changed the fileid mapping to
|
|
// a new wid, i.e. fileid now maps to widExisting,
|
|
// and so the new fileid mapping shouldn't be deleted.
|
|
//
|
|
_fileIdMap.LokDelete( fileId, wid );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete the wid from strings table and property store
|
|
//
|
|
|
|
BOOL fUsnVolume = ( eUsnsArray == eType );
|
|
_strings.LokDelete( 0, wid, fUsnVolume, fUsnVolume );
|
|
|
|
//
|
|
// The use of CI_VOLID_USN_NOT_ENABLED below is okay because the
|
|
// usn is 0 and so the real volume id does not matter. In
|
|
// CheckPointChangesFlushed, usns with a value of 0 are ignored.
|
|
// If the real volume id is needed then it needs to be stored
|
|
// in the property store, which is a big overhead.
|
|
//
|
|
|
|
CDocumentUpdateInfo info( wid, CI_VOLID_USN_NOT_ENABLED, 0, TRUE );
|
|
_xCiManager->UpdateDocument( &info );
|
|
|
|
cDeletedTotal++;
|
|
}
|
|
else if ( _strings.LokSeenNew(wid, eType) )
|
|
{
|
|
# if CIDBG == 1
|
|
XGrowable<WCHAR> awc;
|
|
_strings.Find( wid, awc );
|
|
ciDebugOut (( DEB_CAT, "\tnew: %u (0x%x) %ws\n", wid, wid, awc.Get() ));
|
|
# endif
|
|
|
|
CDocumentUpdateInfo info( wid, CI_VOLID_USN_NOT_ENABLED, 0, FALSE );
|
|
_xCiManager->UpdateDocument( &info );
|
|
|
|
cNewTotal++;
|
|
}
|
|
// ==================== end lock ===================
|
|
}
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
_strings.LokEndSeen( eType );
|
|
}
|
|
|
|
ciDebugOut (( DEB_ITRACE,
|
|
"%d new documents, %d deleted documents\n",
|
|
cNewTotal,
|
|
cDeletedTotal ));
|
|
} //EndUpdate
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::HandleError
|
|
//
|
|
// Synopsis: Checks the status code passed and if it indicates corruption,
|
|
// the index will be marked corrupt in-memory as well as on
|
|
// disk. The recovery will happen on a subsequent restart.
|
|
//
|
|
// Arguments: [status] - The status code to check for corruption.
|
|
//
|
|
// History: 3-21-96 srikants Created
|
|
// 3-20-98 kitmanh Don't Mark catalog corrupt if the catalog
|
|
// is read-only
|
|
//
|
|
// Notes: MUST NOT THROW
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::HandleError( NTSTATUS status )
|
|
{
|
|
TRY
|
|
{
|
|
if ( IsCiCorruptStatus(status) && !IsReadOnly() )
|
|
{
|
|
CLock lock(_mutex);
|
|
|
|
//
|
|
// If we are initialized and not marked that we are corrupt yet,
|
|
// mark CI corrupt.
|
|
//
|
|
if ( IsInit() )
|
|
{
|
|
//
|
|
// Mark that we are corrupt persistently.
|
|
//
|
|
if ( CI_CORRUPT_DATABASE == status )
|
|
{
|
|
|
|
if ( !_scopeTable.IsCiDataCorrupt() )
|
|
_scopeTable.MarkCiDataCorrupt();
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( CI_CORRUPT_CATALOG == status );
|
|
if ( !_scopeTable.IsFsCiDataCorrupt() )
|
|
_scopeTable.MarkFsCiDataCorrupt();
|
|
}
|
|
|
|
if ( !_statusMonitor.IsCorrupt() )
|
|
{
|
|
_statusMonitor.ReportFailure( status );
|
|
_statusMonitor.SetStatus( status );
|
|
}
|
|
}
|
|
}
|
|
else if ( CI_PROPSTORE_INCONSISTENCY == status )
|
|
{
|
|
CLock lock(_mutex);
|
|
_statusMonitor.ReportPropStoreError();
|
|
_statusMonitor.SetStatus( status );
|
|
}
|
|
else if ( IsDiskLowError(status) )
|
|
{
|
|
NoLokProcessDiskFull();
|
|
}
|
|
}
|
|
CATCH( CException,e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error (0x%X) in CiCat::HandleError\n",
|
|
e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //HandleError
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::MarkFullScanNeeded
|
|
//
|
|
// Synopsis: Marks that a full scan is needed.
|
|
//
|
|
// History: 1-27-97 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::MarkFullScanNeeded()
|
|
{
|
|
_scopeTable.RecordFullScanNeeded();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::MarkIncrScanNeeded
|
|
//
|
|
// Synopsis: Marks that an incremental scan is needed for all the scopes.
|
|
//
|
|
// History: 1-27-97 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::MarkIncrScanNeeded()
|
|
{
|
|
_scopeTable.RecordIncrScanNeeded( FALSE );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ProcessChangesFlush
|
|
//
|
|
// Arguments: [ft] - Time stamp of the last successful flush.
|
|
// [cEntries ] - Number of entries in the pUsnEntries.
|
|
// [ppUsnEntries] - Array of USN entries
|
|
//
|
|
// Synopsis: Records the time of the last successful changelog flush, and
|
|
// make a copy of usn flush info.
|
|
//
|
|
// History: 1-28-97 srikants Created
|
|
// 05-07-97 SitaramR Usns
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ProcessChangesFlush( FILETIME const & ft,
|
|
ULONG cEntries,
|
|
USN_FLUSH_INFO const * const * ppUsnEntries )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "ProcessChangesFlush\n" ));
|
|
|
|
//
|
|
// We shouldn't flush until Recovery has been performed.
|
|
// Wait for the recovery to be complete before obtaining
|
|
// a lock. Recovery could take a long time.
|
|
//
|
|
|
|
_evtPh2Init.Wait();
|
|
|
|
// Fix for bug 151799. We will be setting _evtPh2Init if we detect
|
|
// corruption to break a potential deadlock. So we should bail out
|
|
// without flushing if recovery wasn't successfully completed.
|
|
|
|
if ( IsShuttingDown() || !IsRecoveryCompleted() )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "ProcessChangesFlush abort: shutting down\n" ));
|
|
return;
|
|
}
|
|
|
|
CLock lock( _mutex );
|
|
|
|
{
|
|
BOOL fAnyInitialScans = ( _scanMgr.AnyInitialScans() ||
|
|
_usnMgr.AnyInitialScans() );
|
|
|
|
BOOL fIsBackedUpMode = _propstoremgr.IsBackedUpMode();
|
|
|
|
if ( !fAnyInitialScans )
|
|
{
|
|
if ( !fIsBackedUpMode )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "switch from NotBackedUp to BackedUp\n" ));
|
|
_propstoremgr.MarkBackedUpMode();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( fIsBackedUpMode )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "switch from BackedUp to NotBackedUp\n" ));
|
|
_propstoremgr.MarkNotBackedUpMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Flush the property store so that all documents indexed into it so far
|
|
// will be persisted before the marker is advanced. If we do not flush the
|
|
// prop store before advancing the marker, and if the prop store shut down
|
|
// dirty, we will end up with a few docs that will have to be forced out
|
|
// of the prop store because their wids would be unreliable, and they won't
|
|
// get rescanned because the marker was already advanced.
|
|
//
|
|
// Note: flushing the propertystore manager will reset the backup streams,
|
|
// so be sure to do this when switching backup modes.
|
|
//
|
|
|
|
if ( _propstoremgr.IsBackedUpMode() )
|
|
_propstoremgr.Flush();
|
|
|
|
_ftLastCLFlush = ft;
|
|
|
|
_usnFlushInfoList.SetUsns( cEntries, ppUsnEntries );
|
|
|
|
ScheduleSerializeChanges();
|
|
} //ProcessChangesFlush
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ScheduleSerializeChanges
|
|
//
|
|
// Synopsis: Schedules a task with scan manager to serialize changes info
|
|
//
|
|
// History: 4-16-96 srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::ScheduleSerializeChanges()
|
|
{
|
|
_scanMgr.ScheduleSerializeChanges();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::NoLokProcessDiskFull
|
|
//
|
|
// Synopsis: Processes the disk full situation. Stops any in progress scans
|
|
// and also prevents future scans until the situation eases.
|
|
// Further notifications are also disabled.
|
|
//
|
|
// History: 4-16-96 srikants Created
|
|
//
|
|
// Notes: Don't have a lock on CiCat when calling this
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::NoLokProcessDiskFull()
|
|
{
|
|
if ( IsStarted() && !_statusMonitor.IsCorrupt() )
|
|
{
|
|
TRY
|
|
{
|
|
//
|
|
// Don't call _scopeTable.ProcessDiskFull with CiCat lock -
|
|
// there can be a deadlock with the notification manager.
|
|
//
|
|
|
|
_scopeTable.ProcessDiskFull( _docStore, GetPartition() );
|
|
|
|
//
|
|
// Don't take the lock here. These are atomic dword updates
|
|
// and the only risk is two debugouts.
|
|
// The resman lock may be held here.
|
|
//
|
|
|
|
if ( !_statusMonitor.IsLowOnDisk() )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "CiCat:Entering low disk space state\n" ));
|
|
_statusMonitor.SetDiskFull();
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"Error (0x%X) caught in CiCat::ProcessDiskFull\n" ));
|
|
}
|
|
END_CATCH
|
|
}
|
|
} //NoLokProcessDiskFull
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::NoLokClearDiskFull
|
|
//
|
|
// Synopsis: Clears the disk full situation, re-enables notifications and
|
|
// starts a scan on all the paths.
|
|
//
|
|
// History: 4-16-96 srikants Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::NoLokClearDiskFull()
|
|
{
|
|
if ( IsStarted() && !_statusMonitor.IsCorrupt() )
|
|
{
|
|
if ( _statusMonitor.IsLowOnDisk() )
|
|
{
|
|
//
|
|
// don't obtain cicat lock while calling to scope table - there
|
|
// can be a deadlock with notification manager.
|
|
//
|
|
_scopeTable.ClearDiskFull( _docStore );
|
|
|
|
//
|
|
// Don't take the lock here since the resman lock is probably
|
|
// held and this is just an atomic dword write.
|
|
//
|
|
|
|
ciDebugOut(( DEB_WARN, "CiCat:Clearing Low DiskSpace\n" ));
|
|
_statusMonitor.ClearLowDiskSpace();
|
|
}
|
|
}
|
|
} //NoLokClearDiskFull
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetVRoots
|
|
//
|
|
// Synopsis: Retrieves virtual roots from the given vserver
|
|
//
|
|
// Arguments: [ulInstance] -- the vserver instance
|
|
// [eType] -- w3/nntp/imap
|
|
// [xDirs] -- where to write the vroot info
|
|
//
|
|
// History: 27-Jan-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void GetVRoots(
|
|
ULONG ulInstance,
|
|
CiVRootTypeEnum eType,
|
|
XPtr<CIISVirtualDirectories> & xDirs )
|
|
{
|
|
//
|
|
// If the vserver instance no longer exists (so the path wasn't found),
|
|
// remove the vroots for that vserver by returning an empty list of
|
|
// vroots. For any other error, rethrow it.
|
|
//
|
|
|
|
TRY
|
|
{
|
|
CMetaDataMgr mdMgr( FALSE, eType, ulInstance );
|
|
xDirs.Set( new CIISVirtualDirectories( eType ) );
|
|
mdMgr.EnumVPaths( xDirs.GetReference() );
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
if ( HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) != e.GetErrorCode() )
|
|
RETHROW();
|
|
}
|
|
END_CATCH
|
|
} //GetVRoots
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SynchWithIIS, private
|
|
//
|
|
// Synopsis: Grovel metabase looking for IIS virtual roots (w3 and/or nntp)
|
|
//
|
|
// Arguments: [fRescanTC] -- if TRUE, rescan the impersonation token cache
|
|
// [fSleep] -- if TRUE, sleep before doing work.
|
|
//
|
|
// Effects: Automatic virtual roots that are no longer in the metabase
|
|
// (or have changed) are removed here.
|
|
// If Indexing W3 or NNTP is turned off, vroots are removed.
|
|
//
|
|
// Notes: The idea behind the locking is that multiple threads can
|
|
// call this function at once, and only 1 thread should sync
|
|
// at once. If a thread is syncing and another is waiting
|
|
// on the mutex, the first thread can abort early since the
|
|
// second thread will do the sync.
|
|
//
|
|
// History: 15-Feb-95 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::SynchWithIIS(
|
|
BOOL fRescanTC,
|
|
BOOL fSleep )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "SynchWithIIS\n" ));
|
|
|
|
//
|
|
// Only need 1 thread waiting for a second thread to abort
|
|
//
|
|
|
|
if ( _cIISSynchThreads > 1 )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "sync abort really early\n" ));
|
|
return;
|
|
}
|
|
|
|
CReferenceCount count( _cIISSynchThreads );
|
|
|
|
//
|
|
// Sleep in case other IIS changes are being made in bulk.
|
|
//
|
|
|
|
if ( fSleep )
|
|
{
|
|
for ( int i = 0; i < 8; i++ )
|
|
{
|
|
if ( !IsShuttingDown() )
|
|
SleepEx( 1000, TRUE );
|
|
}
|
|
}
|
|
|
|
CLock lock( _mtxIISSynch );
|
|
|
|
TRY
|
|
{
|
|
//
|
|
// If another thread is above waiting on _mtxIISSynch, abort.
|
|
//
|
|
|
|
if ( _cIISSynchThreads > 1 )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "sync abort early\n" ));
|
|
count.Decrement();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Enumerate IIS up front and save info in a local copy
|
|
//
|
|
|
|
XPtr<CIISVirtualDirectories> xW3VDirs;
|
|
if ( _fIndexW3Roots )
|
|
GetVRoots( _W3SvcInstance, W3VRoot, xW3VDirs );
|
|
|
|
XPtr<CIISVirtualDirectories> xNNTPVDirs;
|
|
if ( _fIndexNNTPRoots )
|
|
GetVRoots( _NNTPSvcInstance, NNTPVRoot, xNNTPVDirs );
|
|
|
|
XPtr<CIISVirtualDirectories> xIMAPVDirs;
|
|
if ( _fIndexIMAPRoots )
|
|
GetVRoots( _IMAPSvcInstance, IMAPVRoot, xIMAPVDirs );
|
|
|
|
XGrowable<WCHAR> xwcsVRoot;
|
|
CLowerFunnyPath lcaseFunnyPRoot;
|
|
|
|
unsigned iBmk = 0;
|
|
BOOL fDone = FALSE;
|
|
|
|
//
|
|
// First, find any deletions.
|
|
//
|
|
|
|
while ( !fDone && !IsShuttingDown() && ( 1 == _cIISSynchThreads ) )
|
|
{
|
|
unsigned cwcVRoot, cwcPRoot;
|
|
ULONG Type = _strings.EnumerateVRoot( xwcsVRoot,
|
|
cwcVRoot,
|
|
lcaseFunnyPRoot,
|
|
cwcPRoot,
|
|
iBmk );
|
|
|
|
if ( Type == PCatalog::EndRoot )
|
|
fDone = TRUE;
|
|
else if ( Type & PCatalog::AutomaticRoot )
|
|
{
|
|
BOOL fDelete = FALSE;
|
|
BOOL fIMAP = (0 != (Type & PCatalog::IMAPRoot));
|
|
BOOL fNNTP = (0 != (Type & PCatalog::NNTPRoot));
|
|
Win4Assert( !(fIMAP && fNNTP) );
|
|
BOOL fW3 = !fIMAP && !fNNTP;
|
|
CiVRootTypeEnum eType = fW3 ? W3VRoot : fNNTP ? NNTPVRoot : IMAPVRoot;
|
|
BOOL fNonIndexedVDir = ( 0 != (Type & PCatalog::NonIndexedVDir) );
|
|
|
|
if ( ( fNNTP && !_fIndexNNTPRoots ) ||
|
|
( fIMAP && !_fIndexIMAPRoots ) ||
|
|
( fW3 && !_fIndexW3Roots ) )
|
|
fDelete = TRUE;
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"Looking for type %d %ws vroot %ws\n",
|
|
Type, GetVRootService( eType ),
|
|
xwcsVRoot.Get() ));
|
|
|
|
//
|
|
// Look up the vpath in the cached metabase list
|
|
//
|
|
|
|
CIISVirtualDirectories * pDirs = fNNTP ? xNNTPVDirs.GetPointer() :
|
|
fW3 ? xW3VDirs.GetPointer() :
|
|
xIMAPVDirs.GetPointer();
|
|
Win4Assert( 0 != pDirs );
|
|
|
|
if ( ! pDirs->Lookup( xwcsVRoot.Get(),
|
|
cwcVRoot,
|
|
lcaseFunnyPRoot.GetActualPath(),
|
|
cwcPRoot ) )
|
|
fDelete = TRUE;
|
|
}
|
|
|
|
if ( fDelete )
|
|
{
|
|
// OPTIMIZATION: if path is a registry scope, migrate
|
|
// scope from vroot to registry scope. Otherwise the
|
|
// files will be deleted, rescanned, and readded.
|
|
|
|
ciDebugOut(( DEB_ITRACE, "removing %ws vroot %ws\n",
|
|
GetVRootService( eType ),
|
|
xwcsVRoot.Get() ));
|
|
RemoveVirtualScope( xwcsVRoot.Get(), TRUE, eType, !fNonIndexedVDir );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If another thread is above waiting on _mtxIISSynch, abort.
|
|
//
|
|
|
|
if ( _cIISSynchThreads > 1 )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "sync abort early\n" ));
|
|
count.Decrement();
|
|
return;
|
|
}
|
|
|
|
// only set up impersonation and add vroots if indexing w3, nntp, or imap
|
|
|
|
if ( _fIndexW3Roots || _fIndexNNTPRoots || _fIndexIMAPRoots )
|
|
{
|
|
if ( !IsShuttingDown() && fRescanTC )
|
|
{
|
|
SignalDaemonRescanTC();
|
|
_impersonationTokenCache.ReInitializeIISScopes(
|
|
xW3VDirs.GetPointer(),
|
|
xNNTPVDirs.GetPointer(),
|
|
xIMAPVDirs.GetPointer() );
|
|
}
|
|
|
|
//
|
|
// Now add paths we haven't seen from IIS services
|
|
//
|
|
|
|
if ( _fIndexW3Roots && !IsShuttingDown() )
|
|
xW3VDirs->Enum( *this );
|
|
|
|
if ( _fIndexNNTPRoots && !IsShuttingDown() )
|
|
xNNTPVDirs->Enum( *this );
|
|
|
|
if ( _fIndexIMAPRoots && !IsShuttingDown() )
|
|
xIMAPVDirs->Enum( *this );
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"Exception 0x%x caught groveling IIS metabase.\n",
|
|
e.GetErrorCode() ));
|
|
|
|
HandleError( e.GetErrorCode() );
|
|
}
|
|
END_CATCH
|
|
ciDebugOut(( DEB_ITRACE, "SynchWithIIS (done)\n" ));
|
|
} //SynchWithIIS
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SynchWithRegistryScopes, private
|
|
//
|
|
// Synopsis: Grovel registry looking for CI scopes to filter
|
|
//
|
|
// Arguments: [fRescanTC] -- if TRUE, rescan the impersonation token cache
|
|
//
|
|
// History: 16-Oct-96 dlee Created
|
|
// 30-Jun-98 kitmanh handle read-only catalogs
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::SynchWithRegistryScopes(
|
|
BOOL fRescanTC )
|
|
{
|
|
// All catalogs are named now
|
|
|
|
Win4Assert( 0 != GetName() );
|
|
|
|
TRY
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Reading scopes from '%ws'\n", GetScopesKey() ));
|
|
CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() );
|
|
|
|
if ( !IsReadOnly() )
|
|
{
|
|
// First, find any deletions.
|
|
|
|
WCHAR awcRoot[MAX_PATH];
|
|
unsigned iBmk = 0;
|
|
BOOL fDone = FALSE;
|
|
|
|
//
|
|
// update ignoredscopes table
|
|
//
|
|
while ( !IsShuttingDown() &&
|
|
_scopesIgnored.Enumerate( awcRoot,
|
|
sizeof awcRoot/sizeof WCHAR,
|
|
iBmk ) )
|
|
{
|
|
OnIgnoredScopeDelete(awcRoot, iBmk, regScopes);
|
|
}
|
|
|
|
//
|
|
// update _scopeTable
|
|
//
|
|
iBmk = 0;
|
|
|
|
while ( !IsShuttingDown() &&
|
|
_scopeTable.Enumerate( awcRoot,
|
|
sizeof awcRoot / sizeof WCHAR,
|
|
iBmk ) )
|
|
{
|
|
OnIndexedScopeDelete(awcRoot, iBmk, regScopes);
|
|
}
|
|
|
|
if ( !IsShuttingDown() )
|
|
{
|
|
if ( fRescanTC )
|
|
{
|
|
SignalDaemonRescanTC();
|
|
_impersonationTokenCache.ReInitializeScopes();
|
|
}
|
|
}
|
|
|
|
// Now add paths we haven't seen.
|
|
|
|
if ( !IsShuttingDown() )
|
|
{
|
|
CRegistryScopesCallBackAdd callback( *this );
|
|
regScopes.EnumerateValues( 0, callback );
|
|
|
|
// Do seen processing to remove stale fixups.
|
|
|
|
SetupScopeFixups();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !IsShuttingDown() )
|
|
{
|
|
CRegistryScopesCallBackFillUsnArray callback( *this );
|
|
regScopes.EnumerateValues( 0, callback );
|
|
}
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Exception 0x%x caught groveling ci registry.\n",
|
|
e.GetErrorCode() ));
|
|
|
|
HandleError( e.GetErrorCode() );
|
|
}
|
|
END_CATCH
|
|
|
|
} //SynchWithRegistryScopes
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::OnIndexedScopeDelete, private
|
|
//
|
|
// Synopsis: Grovel registry looking for indexed CI scopes to remove
|
|
//
|
|
// Arguments: [pwcRoot] -- indexed scope table entry to search for and
|
|
// possibly remove
|
|
// [iBmk] -- enumeration index
|
|
// [regScopes] -- registry key to enumerate
|
|
//
|
|
// returns: none.
|
|
//
|
|
// History: 16-Oct-96 dlee Created
|
|
// 3-20-98 mohamedn cut from SynchWithRegistryScopes
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::OnIndexedScopeDelete(WCHAR const * pwcRoot, unsigned & iBmk, CRegAccess & regScopes)
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Looking for on-disk '%ws'\n", pwcRoot ));
|
|
|
|
BOOL fDelete = FALSE;
|
|
CRegistryScopesCallBackFind callback( pwcRoot );
|
|
|
|
TRY
|
|
{
|
|
regScopes.EnumerateValues( 0, callback );
|
|
|
|
// If the physical scope isn't in the registry and it isn't
|
|
// an IIS root, delete it.
|
|
|
|
if ( ( !callback.WasFound() ) &&
|
|
( !_strings.DoesPhysicalRootExist( pwcRoot ) ) )
|
|
{
|
|
fDelete = TRUE;
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Exception 0x%x enumerating regscopes for Root '%ws'\n",
|
|
e.GetErrorCode(),
|
|
pwcRoot ));
|
|
|
|
// No scopes registry key is the likely exception. Only
|
|
// remove the root if it isn't an IIS root.
|
|
|
|
fDelete = !_strings.DoesPhysicalRootExist( pwcRoot );
|
|
}
|
|
END_CATCH
|
|
|
|
if ( fDelete )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "removing on-disk root '%ws'\n", pwcRoot ));
|
|
RemoveScopeFromCI( pwcRoot, TRUE );
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::OnIgnoredScopeDelete, private
|
|
//
|
|
// Synopsis: Grovel registry looking for excluded CI scopes to remove
|
|
//
|
|
// Arguments: [pwcRoot] -- ignored scope table entry to search for and
|
|
// possibly remove
|
|
// [iBmk] -- enumeration index
|
|
// [regScopes] -- registry key to enumerate
|
|
//
|
|
// returns: none.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::OnIgnoredScopeDelete(WCHAR const * pwcRoot, unsigned & iBmk, CRegAccess & regScopes)
|
|
{
|
|
|
|
ciDebugOut(( DEB_ITRACE, "OnIgnoredScopeDelete(%ws)\n", pwcRoot ));
|
|
|
|
BOOL fDelete = FALSE;
|
|
CRegistryScopesCallBackFind callback( pwcRoot );
|
|
|
|
TRY
|
|
{
|
|
regScopes.EnumerateValues( 0, callback );
|
|
|
|
if ( !callback.WasFound() )
|
|
{
|
|
fDelete = TRUE;
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Exception 0x%x enumerating regscopes for Root '%ws'\n",
|
|
e.GetErrorCode(),
|
|
pwcRoot ));
|
|
|
|
// No scopes registry key is the likely exception. Only
|
|
// remove the root if it isn't an IIS root.
|
|
|
|
fDelete = !_strings.DoesPhysicalRootExist( pwcRoot );
|
|
}
|
|
END_CATCH
|
|
|
|
if ( fDelete )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "removing IgnoredScope '%ws'\n", pwcRoot ));
|
|
|
|
//
|
|
// need to rescan scopes that match the regx being removed.
|
|
//
|
|
|
|
_scopesIgnored.RemoveElement(pwcRoot);
|
|
|
|
iBmk--;
|
|
|
|
CScopeEntry deletedIgnoredScope(pwcRoot);
|
|
|
|
ciDebugOut(( DEB_ITRACE, "GetScopeToRescan(%ws)\n", deletedIgnoredScope.Get() ));
|
|
|
|
ScanScopeTableEntry(deletedIgnoredScope.Get());
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ScanScopeTableEntry, private
|
|
//
|
|
// Synopsis: Initiates a Scan on suitable scope table entries, or passed-in
|
|
// scope.
|
|
//
|
|
// Arguments: [pwszScopeToRescan] -- input scope to rescan
|
|
//
|
|
// returns: none.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::ScanScopeTableEntry(WCHAR const * pwszScopeToRescan)
|
|
{
|
|
unsigned iBmk = 0;
|
|
|
|
WCHAR awcRoot[MAX_PATH+1];
|
|
|
|
while ( !IsShuttingDown() &&
|
|
_scopeTable.Enumerate( awcRoot,
|
|
sizeof awcRoot / sizeof WCHAR,
|
|
iBmk ) )
|
|
{
|
|
CScopeMatch scopeToRescan( pwszScopeToRescan, wcslen(pwszScopeToRescan) );
|
|
|
|
if ( scopeToRescan.IsPrefix( awcRoot, wcslen(awcRoot) ) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "ScanThisScope(%ws)\n", pwszScopeToRescan ));
|
|
|
|
ScanThisScope(pwszScopeToRescan);
|
|
|
|
break;
|
|
}
|
|
else if ( scopeToRescan.IsInScope( awcRoot, wcslen(awcRoot) ) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "ScanThisScope(%ws)\n", awcRoot ));
|
|
|
|
ScanThisScope(awcRoot);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ScanThisScope, private
|
|
//
|
|
// Synopsis: Forces a scan on the supplied path
|
|
//
|
|
// Arguments: [pwszPath] -- path to scan
|
|
//
|
|
// returns: none
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::ScanThisScope(WCHAR const * pwszPath)
|
|
{
|
|
Win4Assert( pwszPath );
|
|
|
|
BOOL fDoDeletions = TRUE;
|
|
BOOL fSupportsUsns = VolumeSupportsUsns( pwszPath[0] );
|
|
|
|
VOLUMEID volumeId;
|
|
|
|
if ( fSupportsUsns )
|
|
volumeId = MapPathToVolumeId( pwszPath );
|
|
else
|
|
volumeId = CI_VOLID_USN_NOT_ENABLED;
|
|
|
|
if ( fSupportsUsns )
|
|
{
|
|
InitUsnTreeScan( pwszPath );
|
|
|
|
_usnMgr.AddScope( pwszPath,
|
|
volumeId,
|
|
fDoDeletions );
|
|
}
|
|
else
|
|
{
|
|
_scanMgr.ScanScope( pwszPath,
|
|
GetPartition(),
|
|
UPD_FULL,
|
|
fDoDeletions,
|
|
FALSE ); // not a delayed scan - immediate scan
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SetupScopeFixups, private
|
|
//
|
|
// Synopsis: Grovel registry looking for CI scopes and setup fixups
|
|
//
|
|
// History: 16-Oct-96 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::SetupScopeFixups()
|
|
{
|
|
// is the catalog not named? if so, it isn't in the registry
|
|
|
|
if ( 0 == GetName() )
|
|
return;
|
|
|
|
TRY
|
|
{
|
|
_scopeFixup.BeginSeen();
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Reading scope fixups from '%ws'\n", GetScopesKey() ));
|
|
CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() );
|
|
|
|
CRegistryScopesCallBackFixups callback( this );
|
|
regScopes.EnumerateValues( 0, callback );
|
|
|
|
_scopeFixup.EndSeen();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Exception 0x%x caught groveling ci registry fixups.\n",
|
|
e.GetErrorCode() ));
|
|
|
|
HandleError( e.GetErrorCode() );
|
|
}
|
|
END_CATCH
|
|
} //SetupScopeFixups
|
|
|
|
|
|
//
|
|
// Support for CI Frame Work
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::StartupCiFrameWork
|
|
//
|
|
// Synopsis: Takes the CiManager object pointer and refcounts it.
|
|
//
|
|
// Arguments: [pICiManager] -
|
|
//
|
|
// History: 12-05-96 srikants Created
|
|
//
|
|
// Note: Choose a better name.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
void CiCat::StartupCiFrameWork( ICiManager * pICiManager )
|
|
{
|
|
Win4Assert( 0 != pICiManager );
|
|
Win4Assert( 0 == _xCiManager.GetPointer() );
|
|
|
|
if ( 0 == _xCiManager.GetPointer() )
|
|
{
|
|
pICiManager->AddRef();
|
|
_xCiManager.Set( pICiManager );
|
|
}
|
|
|
|
RefreshRegistryParams();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RefreshRegistryParams
|
|
//
|
|
// Synopsis: Refreshes CI registry parameters.
|
|
//
|
|
// History: 12-12-96 srikants Created
|
|
// 1-25-97 mohamedn ICiAdminParams, ICiAdmin
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RefreshRegistryParams()
|
|
{
|
|
|
|
ICiAdminParams *pICiAdminParams = 0;
|
|
XInterface<ICiAdminParams> xICiAdminParams;
|
|
|
|
ICiAdmin *pICiAdmin = 0;
|
|
XInterface<ICiAdmin> xICiAdmin;
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
{
|
|
CLock lock(_mutex);
|
|
|
|
if ( !IsShuttingDown() && 0 != _xCiManager.GetPointer() )
|
|
{
|
|
// get pICiAdminParams
|
|
SCODE sc = _xCiManager->GetAdminParams( &pICiAdminParams );
|
|
if ( FAILED(sc) )
|
|
{
|
|
Win4Assert( 0 == pICiAdminParams );
|
|
|
|
THROW( CException(sc) );
|
|
}
|
|
|
|
xICiAdminParams.Set(pICiAdminParams);
|
|
|
|
// get pICiAdmin
|
|
sc = _xCiManager->QueryInterface(IID_ICiAdmin,(void **)&pICiAdmin);
|
|
if ( FAILED(sc) )
|
|
{
|
|
Win4Assert( 0 == pICiAdmin );
|
|
|
|
THROW( CException(sc) );
|
|
}
|
|
|
|
xICiAdmin.Set(pICiAdmin);
|
|
}
|
|
}
|
|
// -----------------------------------------------------
|
|
|
|
if ( !xICiAdmin.IsNull() )
|
|
{
|
|
SCODE sc = xICiAdmin->InvalidateLangResources();
|
|
if ( FAILED (sc) )
|
|
{
|
|
Win4Assert( !"Failed to InvalidateLangResources\n" );
|
|
|
|
THROW( CException(sc) );
|
|
}
|
|
}
|
|
|
|
if ( 0 != pICiAdminParams )
|
|
_regParams.Refresh(pICiAdminParams);
|
|
|
|
//
|
|
// Did we switch on/off auto-aliasing?
|
|
//
|
|
|
|
if ( _fAutoAlias != _regParams.IsAutoAlias() )
|
|
{
|
|
_fAutoAlias = _regParams.IsAutoAlias();
|
|
SynchShares();
|
|
}
|
|
} //RefreshRegistryParams
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::MakeBackupOfPropStore
|
|
//
|
|
// Synopsis: Pass through to CPropertyStore::MakeBackupCopy
|
|
//
|
|
// Arguments: [pwszDir] - The directory in which to create the
|
|
// backup.
|
|
// [pIProgressNotify] -
|
|
// [fAbort] -
|
|
// [pIWorkIds] -
|
|
//
|
|
// History: 3-26-97 srikants Created
|
|
// 3-12-98 kitmanh Passed FALSE to the constructor of
|
|
// CiStorage, since backup should always
|
|
// be writable
|
|
// 01-Nov-98 KLam Pass DiskSpaceToLeave to CiStorage
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::MakeBackupOfPropStore( WCHAR const * pwszDir,
|
|
IProgressNotify * pIProgressNotify,
|
|
BOOL & fAbort,
|
|
ICiEnumWorkids * pIWorkIds )
|
|
{
|
|
XPtr<CiStorage> xStorage( new CiStorage( pwszDir,
|
|
_xAdviseStatus.GetReference(),
|
|
_regParams.GetMinDiskSpaceToLeave(),
|
|
CURRENT_VERSION_STAMP,
|
|
FALSE) );
|
|
|
|
_propstoremgr.MakeBackupCopy( pIProgressNotify,
|
|
fAbort,
|
|
xStorage.GetReference(),
|
|
pIWorkIds,
|
|
0);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsDirectory
|
|
//
|
|
// Synopsis: Returns TRUE if the file is a directory
|
|
//
|
|
// Arguments: [pOldFunnyPath] - File to check
|
|
// [pNewFunnyPath] - If a file/dir is renamed, then the attributes
|
|
// of this file are checked to determine whether
|
|
// pwcsOldFileName is a directory or not
|
|
//
|
|
// History: 20-Mar-96 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsDirectory(
|
|
const CLowerFunnyPath * pOldFunnyPath,
|
|
const CLowerFunnyPath * pNewFunnyPath )
|
|
{
|
|
BOOL fUsnVolume = IsOnUsnVolume( pOldFunnyPath->GetActualPath() );
|
|
WORKID wid;
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
FILEID fileId = fileIdInvalid;
|
|
|
|
if ( fUsnVolume )
|
|
wid = LokLookupWid( *pOldFunnyPath, fileId );
|
|
else
|
|
wid = _strings.LokFind( pOldFunnyPath->GetActualPath() );
|
|
}
|
|
|
|
if ( wid == widInvalid )
|
|
{
|
|
if ( pNewFunnyPath == 0 )
|
|
return FALSE;
|
|
else
|
|
{
|
|
//
|
|
// For rename file notifications, use attributes of pwcsNewFileName
|
|
// to determine whether pwcsOldFileName is a directory or not
|
|
//
|
|
ULONG ulFileAttrib = GetFileAttributes( pNewFunnyPath->GetPath() );
|
|
|
|
if ( ulFileAttrib == 0xFFFFFFFF )
|
|
return FALSE;
|
|
else
|
|
return ( ulFileAttrib & FILE_ATTRIBUTE_DIRECTORY );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PROPVARIANT propVar;
|
|
if ( _propstoremgr.ReadPrimaryProperty( wid, pidAttrib, propVar ) )
|
|
{
|
|
if ( propVar.vt == VT_EMPTY )
|
|
{
|
|
//
|
|
// Case where directories are not being filtered
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
Win4Assert( propVar.vt == VT_UI4 );
|
|
|
|
return ( propVar.ulVal & FILE_ATTRIBUTE_DIRECTORY );
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
} //IsDirectory
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsOnUsnVolume
|
|
//
|
|
// Synopsis: Is the path on a volume that supports usns
|
|
//
|
|
// Arguments: [pwszPath] - Path to check
|
|
//
|
|
// History: 03-Feb-98 dlee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsOnUsnVolume( WCHAR const *pwszPath )
|
|
{
|
|
Win4Assert( 0 != pwszPath );
|
|
|
|
// Usns are not supported for remote paths
|
|
|
|
if ( L'\\' == pwszPath[0] )
|
|
return FALSE;
|
|
|
|
// Look in cache of known usn volumes
|
|
|
|
for ( unsigned i = 0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] )
|
|
return _aUsnVolumes[i].FUsnVolume();
|
|
}
|
|
|
|
return FALSE;
|
|
} //IsOnUsnVolume
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::VolumeSupportsUsns
|
|
//
|
|
// Synopsis: Checks if the volume supports USNs
|
|
//
|
|
// Arguments: [wcVolume] -- volume letter to check
|
|
//
|
|
// History: 05-May-97 SitaramR Created
|
|
//
|
|
// Notes: This method has the side-effect of registering this volume
|
|
// for USN processing.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::VolumeSupportsUsns( WCHAR wcVolume )
|
|
{
|
|
//
|
|
// Usns are not supported for remote paths
|
|
//
|
|
|
|
if ( L'\\' == wcVolume )
|
|
return FALSE;
|
|
|
|
//
|
|
// Look in the cache of known usn volumes
|
|
//
|
|
|
|
for ( unsigned i = 0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == wcVolume )
|
|
return _aUsnVolumes[i].FUsnVolume();
|
|
}
|
|
|
|
CLock lock( _mutex );
|
|
|
|
//
|
|
// Look in the cache again under lock, in case some other thread slipped
|
|
// it in the array.
|
|
//
|
|
|
|
for ( i = 0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == wcVolume )
|
|
return _aUsnVolumes[i].FUsnVolume();
|
|
}
|
|
|
|
//
|
|
// Find out if it's a USN volume and add the result to the cache
|
|
//
|
|
|
|
CImpersonateSystem impersonate;
|
|
|
|
BOOL fUsnVolume = FALSE;
|
|
USN_JOURNAL_DATA UsnJournalInfo;
|
|
FILE_FS_VOLUME_INFORMATION VolumeInfo;
|
|
WCHAR wszVolumePath[] = L"\\\\.\\a:";
|
|
wszVolumePath[4] = wcVolume;
|
|
|
|
VolumeInfo.VolumeCreationTime.QuadPart = 0;
|
|
VolumeInfo.VolumeSerialNumber = 0;
|
|
|
|
HANDLE hVolume = CreateFile( wszVolumePath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL );
|
|
|
|
if ( hVolume == INVALID_HANDLE_VALUE )
|
|
fUsnVolume = FALSE;
|
|
else
|
|
{
|
|
SWin32Handle xHandleVolume( hVolume );
|
|
|
|
//
|
|
// Look up the volume serial number and create time. We'll use these
|
|
// later to decide if the volume has been reformatted underneath us.
|
|
// We don't bother to check error, because there's nothing we could
|
|
// do except set fields to 0, which has already been done.
|
|
//
|
|
|
|
IO_STATUS_BLOCK iosb;
|
|
NtQueryVolumeInformationFile( hVolume,
|
|
&iosb,
|
|
&VolumeInfo,
|
|
sizeof(VolumeInfo),
|
|
FileFsVolumeInformation );
|
|
|
|
//
|
|
// This call will only succeed on NTFS NT5 w/ USN Journal enabled.
|
|
//
|
|
|
|
|
|
NTSTATUS Status = NtFsControlFile( hVolume,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iosb,
|
|
FSCTL_QUERY_USN_JOURNAL,
|
|
0,
|
|
0,
|
|
&UsnJournalInfo,
|
|
sizeof(UsnJournalInfo) );
|
|
|
|
Win4Assert( STATUS_PENDING != Status );
|
|
|
|
if ( NT_SUCCESS(Status) && NT_SUCCESS(iosb.Status) )
|
|
fUsnVolume = TRUE;
|
|
else if ( !IsReadOnly() &&
|
|
( STATUS_JOURNAL_NOT_ACTIVE == Status ||
|
|
STATUS_INVALID_DEVICE_STATE == Status ) )
|
|
{
|
|
//
|
|
// Usn journal not created, create it
|
|
//
|
|
|
|
CREATE_USN_JOURNAL_DATA usnCreateData;
|
|
usnCreateData.MaximumSize = _regParams.GetMaxUsnLogSize();
|
|
usnCreateData.AllocationDelta = _regParams.GetUsnLogAllocationDelta();
|
|
|
|
Status = NtFsControlFile( hVolume,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iosb,
|
|
FSCTL_CREATE_USN_JOURNAL,
|
|
&usnCreateData,
|
|
sizeof(usnCreateData),
|
|
NULL,
|
|
NULL );
|
|
if ( NT_SUCCESS( Status ) && NT_SUCCESS(iosb.Status) )
|
|
{
|
|
Status = NtFsControlFile( hVolume,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iosb,
|
|
FSCTL_QUERY_USN_JOURNAL,
|
|
0,
|
|
0,
|
|
&UsnJournalInfo,
|
|
sizeof(UsnJournalInfo) );
|
|
|
|
Win4Assert( STATUS_PENDING != Status );
|
|
|
|
if ( NT_SUCCESS(Status) && NT_SUCCESS(iosb.Status) )
|
|
fUsnVolume = TRUE;
|
|
else
|
|
fUsnVolume = FALSE;
|
|
}
|
|
else
|
|
fUsnVolume = FALSE;
|
|
}
|
|
else
|
|
fUsnVolume = FALSE;
|
|
}
|
|
|
|
_aUsnVolumes[ _cUsnVolumes ].Set( wcVolume,
|
|
VolumeInfo.VolumeCreationTime.QuadPart,
|
|
VolumeInfo.VolumeSerialNumber,
|
|
fUsnVolume,
|
|
UsnJournalInfo.UsnJournalID );
|
|
_cUsnVolumes++;
|
|
|
|
return fUsnVolume;
|
|
} //VolumeSupportsUsns
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::GetJournalId, public
|
|
//
|
|
// Arguments: [pwszPath] -- Path to USN-enabled volume
|
|
//
|
|
// Returns: Current USN Journal ID.
|
|
//
|
|
// History: 17-Mar-98 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONGLONG const & CiCat::GetJournalId( WCHAR const * pwszPath )
|
|
{
|
|
static ULONGLONG _zero = 0;
|
|
|
|
Win4Assert( L'\\' != pwszPath[0] );
|
|
|
|
//
|
|
// Look in cache of known usn volumes
|
|
//
|
|
|
|
for ( unsigned i=0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] )
|
|
return _aUsnVolumes[i].JournalId();
|
|
}
|
|
|
|
Win4Assert( !"Not USN volume!" );
|
|
|
|
return _zero;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::GetVolumeCreationTime, public
|
|
//
|
|
// Arguments: [pwszPath] -- Path to volume
|
|
//
|
|
// Returns: Current create time (changed on reformat)
|
|
//
|
|
// History: 17-Mar-98 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONGLONG const & CiCat::GetVolumeCreationTime( WCHAR const * pwszPath )
|
|
{
|
|
static ULONGLONG _zero = 0;
|
|
|
|
if ( pwszPath[0] == L'\\' )
|
|
{
|
|
static ULONGLONG ullZero = 0;
|
|
return ullZero;
|
|
}
|
|
|
|
//
|
|
// Look in cache of known usn volumes
|
|
//
|
|
|
|
for ( unsigned i=0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] )
|
|
return _aUsnVolumes[i].CreationTime();
|
|
}
|
|
|
|
Win4Assert( !"Not known volume!" );
|
|
|
|
return _zero;
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::GetVolumeSerialNumber, public
|
|
//
|
|
// Arguments: [pwszPath] -- Path to volume
|
|
//
|
|
// Returns: Current serial number (changed on reformat)
|
|
//
|
|
// History: 17-Mar-98 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONG CiCat::GetVolumeSerialNumber( WCHAR const * pwszPath )
|
|
{
|
|
if ( pwszPath[0] == L'\\' )
|
|
return 0;
|
|
|
|
//
|
|
// Look in cache of known usn volumes
|
|
//
|
|
|
|
for ( unsigned i=0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].DriveLetter() == pwszPath[0] )
|
|
return _aUsnVolumes[i].SerialNumber();
|
|
}
|
|
|
|
Win4Assert( !"Not known volume!" );
|
|
|
|
return 0;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::MapPathToVolumeId
|
|
//
|
|
// Synopsis: Maps a path to a volume id
|
|
//
|
|
// Arguments: [pwszPath] -- Path to map
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
// Notes: The volume id is obtained from the drive letter
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOLUMEID CiCat::MapPathToVolumeId( const WCHAR *pwszPath )
|
|
{
|
|
Win4Assert( wcslen(pwszPath) > 1
|
|
&& pwszPath[0] != L'\\'
|
|
&& pwszPath[1] == L':' );
|
|
|
|
VOLUMEID volId = pwszPath[0];
|
|
|
|
Win4Assert( volId <= 0xff );
|
|
|
|
return volId;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ProcessFile
|
|
//
|
|
// Synopsis: Process given file during usn tree traversal. It returns
|
|
// the workid of file which is used to determine if the file
|
|
// should be processed as an update notification.
|
|
//
|
|
// Arguments: [lcaseFunnyPath] -- Path of file
|
|
// [fileId] -- Fileid of file
|
|
// [volumeId] -- Volume id of file
|
|
// [widParent] -- Workid of parent
|
|
// [wid] -- Workid of file returned here
|
|
//
|
|
// Returns: TRUE if the file was added to CI
|
|
//
|
|
// History: 28-Jul-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::ProcessFile( const CLowerFunnyPath & lcaseFunnyPath,
|
|
FILEID fileId,
|
|
VOLUMEID volumeId,
|
|
WORKID widParent,
|
|
DWORD dwFileAttributes,
|
|
WORKID & wid )
|
|
{
|
|
Win4Assert( 0 != &_mutex );
|
|
CLock lock( _mutex );
|
|
|
|
wid = _fileIdMap.LokFind( fileId, volumeId );
|
|
if ( wid == widInvalid )
|
|
{
|
|
BOOL fNew;
|
|
wid = PathToWorkId( lcaseFunnyPath,
|
|
TRUE, // Add to CI
|
|
fNew,
|
|
0,
|
|
eUsnsArray,
|
|
fileId,
|
|
widParent,
|
|
TRUE ); // CI doesn't know about this
|
|
|
|
if (wid == widInvalid)
|
|
{
|
|
//
|
|
// Unknown file
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// New file, don't re-write property store info just written
|
|
//
|
|
|
|
LokWriteFileUsnInfo( wid, fileId, FALSE, volumeId, widParent, dwFileAttributes );
|
|
WriteFileAttributes( wid, dwFileAttributes );
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the path has changed (due to a rename of the file or a parent
|
|
// directory), update the path in the property store.
|
|
//
|
|
|
|
// override const here - unethical, but saves us a copy
|
|
|
|
CFunnyPath oldPath;
|
|
WorkIdToPath( wid, oldPath );
|
|
|
|
BOOL fRemoveBackSlash = ((CLowerFunnyPath&)lcaseFunnyPath).RemoveBackSlash();
|
|
SCODE sc = S_OK;
|
|
|
|
if ( ( lcaseFunnyPath.GetActualLength() != oldPath.GetActualLength() ) ||
|
|
( 0 != memcmp( lcaseFunnyPath.GetActualPath(),
|
|
oldPath.GetActualPath(),
|
|
oldPath.GetActualLength() * sizeof WCHAR ) ) )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "renaming '%ws' to '%ws' during scan\n",
|
|
oldPath.GetActualPath(),
|
|
lcaseFunnyPath.GetActualPath() ));
|
|
|
|
PROPVARIANT var;
|
|
var.vt = VT_LPWSTR;
|
|
var.pwszVal = (WCHAR*) lcaseFunnyPath.GetActualPath();
|
|
sc = _propstoremgr.WriteProperty( wid,
|
|
pidPath,
|
|
*(CStorageVariant const *)(ULONG_PTR)&var );
|
|
}
|
|
|
|
// override const here - unethical, but saves us a copy
|
|
|
|
if ( fRemoveBackSlash )
|
|
((CLowerFunnyPath&)lcaseFunnyPath).AppendBackSlash();
|
|
|
|
if ( FAILED( sc ) )
|
|
THROW( CException( sc ) );
|
|
}
|
|
|
|
return FALSE;
|
|
} //ProcessFile
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::LokWriteFileUsnInfo
|
|
//
|
|
// Synopsis: Write file's usn info to property store
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [fileId] -- Fileid of wid
|
|
// [fWriteToPropStore] -- If TRUE, write to the property store
|
|
// [volumeId] -- Volume id of wid
|
|
// [widParent] -- Workid of parent
|
|
// [dwFileAttributes] -- Attributes of the file
|
|
//
|
|
// History: 05-May-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::LokWriteFileUsnInfo( WORKID wid,
|
|
FILEID fileId,
|
|
BOOL fWriteToPropStore,
|
|
VOLUMEID volumeId,
|
|
WORKID widParent,
|
|
DWORD dwFileAttributes )
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
|
|
WORKID widExisting = _fileIdMap.LokFind( fileId, volumeId );
|
|
|
|
if ( widExisting == widInvalid )
|
|
{
|
|
//
|
|
// Add fileid -> wid map
|
|
//
|
|
_fileIdMap.LokAdd( fileId, wid, volumeId );
|
|
}
|
|
else if ( wid != widExisting )
|
|
{
|
|
//
|
|
// Remove previous stale fileid mapping and add current mapping.
|
|
// This can happen when a file is renamed to a different file
|
|
// during the usn tree traversal.
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"LokWriteFileUsnInfo, blasting fid %#I64x, %#x to %#x\n",
|
|
fileId, widExisting, wid ));
|
|
|
|
_fileIdMap.LokDelete( fileId, widExisting );
|
|
_fileIdMap.LokAdd( fileId, wid, volumeId );
|
|
}
|
|
|
|
if ( fWriteToPropStore )
|
|
{
|
|
PROPVARIANT propVar;
|
|
propVar.vt = VT_UI8;
|
|
propVar.uhVal.QuadPart = fileId;
|
|
|
|
XWritePrimaryRecord rec( _propstoremgr, wid );
|
|
|
|
SCODE sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidFileIndex,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
|
|
propVar.vt = VT_UI4;
|
|
propVar.ulVal = volumeId;
|
|
sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidVolumeId,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
|
|
propVar.ulVal = widParent;
|
|
sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidParentWorkId,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
|
|
propVar.ulVal = dwFileAttributes;
|
|
sc = _propstoremgr.WritePrimaryProperty( rec.GetReference(),
|
|
pidAttrib,
|
|
*(CStorageVariant const *)(ULONG_PTR)&propVar );
|
|
if (FAILED(sc))
|
|
THROW(CException(sc));
|
|
}
|
|
} //LokWriteFileUsnInfo
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::UpdateDuringRecovery, public
|
|
//
|
|
// Synopsis: Updates an individual workid with usn == 0 during prop
|
|
// store recovery.
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
// [fileid] -- File Id. Valid only for NTFS 5.0 volumes.
|
|
// [fDelete] -- TRUE to indicate deletion. Modify o/w.
|
|
// [pUserData]-- Pointer to CiCat to use for updates.
|
|
//
|
|
// History: 06-18-97 KrishnaN Created
|
|
//
|
|
// Notes: This will be called at property store recovery time to cause
|
|
// deletion and modification of wids. The property store will always
|
|
// be flushed before the usn marker is advanced in
|
|
// CheckPointchangesFlushed. Which means that docs newly added to the
|
|
// property store can be deleted with a usn of 0. Using usn of 0 causes
|
|
// the marker to not move forward, but that is OK because the docs
|
|
// being deleted were not marked as having been filtered. As a result,
|
|
// rescan will cause these docs to be picked up agains and scheduled
|
|
// for filtering.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void UpdateDuringRecovery( WORKID wid, BOOL fDelete, void const *pUserData )
|
|
{
|
|
CiCat *pCiCat = (CiCat *)pUserData;
|
|
|
|
Win4Assert(pCiCat);
|
|
|
|
pCiCat->Update( wid, pCiCat->GetPartition(), CI_VOLID_USN_NOT_ENABLED, 0,
|
|
fDelete ? CI_DELETE_OBJ : CI_UPDATE_OBJ);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FileIdToPath
|
|
//
|
|
// Synopsis: Converts fileid to path and checks if path is in scope
|
|
//
|
|
// Arguments: [fileId] -- Fileid of wid
|
|
// [pUsnVolume] -- Volume
|
|
// [lcaseFunnyPath] -- Path returned here
|
|
// [fPathInScope] -- Scope info returned here
|
|
//
|
|
// History: 23-Jun-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::FileIdToPath( FILEID fileId,
|
|
CUsnVolume *pUsnVolume,
|
|
CLowerFunnyPath & lcaseFunnyPath,
|
|
BOOL & fPathInScope )
|
|
{
|
|
fPathInScope = FALSE;
|
|
|
|
WORKID wid;
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
wid = _fileIdMap.LokFind( fileId, pUsnVolume->VolumeId() );
|
|
if ( widInvalid == wid )
|
|
return;
|
|
|
|
#if 0 // valid, but expensive
|
|
PROPVARIANT propVar;
|
|
BOOL fFound = _propstoremgr.ReadPrimaryProperty( wid,
|
|
pidFileIndex,
|
|
propVar );
|
|
Win4Assert( fFound && propVar.uhVal.QuadPart == fileId );
|
|
#endif // CIDBG == 1
|
|
|
|
unsigned cc = _strings.Find( wid, lcaseFunnyPath );
|
|
|
|
if ( 0 == cc )
|
|
THROW ( CException( STATUS_INVALID_PARAMETER ) );
|
|
}
|
|
|
|
if ( !_usnMgr.IsPathIndexed( pUsnVolume, lcaseFunnyPath ) )
|
|
return;
|
|
|
|
if ( ! IsEligibleForFiltering( lcaseFunnyPath ) )
|
|
return;
|
|
|
|
fPathInScope = TRUE;
|
|
} //FileIdToPath
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::FileIdToPath
|
|
//
|
|
// Synopsis: Converts a fileid and volumeid to a path.
|
|
//
|
|
// Arguments: [fileId] -- Fileid of wid
|
|
// [pUsnVolume] -- Volume
|
|
// [funnyPath] -- Path returned here
|
|
//
|
|
// Returns: Size of path (or 0 if not found).
|
|
//
|
|
// History: 19-Mar-1998 dlee Created
|
|
// 31-Dec-1998 KyleP Re-enabled and fixed for > MAX_PATH
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CiCat::FileIdToPath( FILEID & fileId,
|
|
VOLUMEID volumeId,
|
|
CLowerFunnyPath & funnyPath )
|
|
{
|
|
Win4Assert( 0 != volumeId );
|
|
Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId );
|
|
Win4Assert( fileIdInvalid != fileId );
|
|
Win4Assert( 0 != fileId );
|
|
|
|
//
|
|
// Make sure the volume information is faulted in -- it may not exist
|
|
// yet when the catalog is just starting.
|
|
//
|
|
|
|
VolumeSupportsUsns( (WCHAR) volumeId );
|
|
|
|
//
|
|
// Look for the volume handle
|
|
//
|
|
|
|
unsigned cc = 0;
|
|
HANDLE hVolume = INVALID_HANDLE_VALUE;
|
|
|
|
for ( unsigned i = 0; i < _cUsnVolumes; i++ )
|
|
{
|
|
if ( _aUsnVolumes[i].VolumeId() == volumeId )
|
|
{
|
|
hVolume = _aUsnVolumes[i].Volume();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Was the volume found in the list of volumes being indexed?
|
|
|
|
if ( INVALID_HANDLE_VALUE != hVolume )
|
|
{
|
|
// Put the fileid in the unicode string -- ntfs expects this
|
|
|
|
UNICODE_STRING uScope;
|
|
uScope.Buffer = (WCHAR *) &fileId;
|
|
uScope.Length = sizeof fileId;
|
|
uScope.MaximumLength = sizeof fileId;
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttr;
|
|
InitializeObjectAttributes( &ObjectAttr, // Structure
|
|
&uScope, // Name
|
|
OBJ_CASE_INSENSITIVE, // Attributes
|
|
hVolume, // Root
|
|
0 ); // Security
|
|
|
|
IO_STATUS_BLOCK IoStatus;
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
|
NTSTATUS Status = NtOpenFile( &h,
|
|
FILE_READ_ATTRIBUTES,
|
|
&ObjectAttr,
|
|
&IoStatus,
|
|
FILE_SHARE_READ |
|
|
FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
FILE_OPEN_BY_FILE_ID );
|
|
if ( NT_SUCCESS( Status ) )
|
|
Status = IoStatus.Status;
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
SHandle xHandle( h );
|
|
|
|
// retrieve the path info for the file opened by id
|
|
|
|
XGrowable<BYTE> xbuf;
|
|
|
|
do
|
|
{
|
|
PFILE_NAME_INFORMATION FileName = (PFILE_NAME_INFORMATION) xbuf.Get();
|
|
FileName->FileNameLength = xbuf.SizeOf() - sizeof(FILE_NAME_INFORMATION);
|
|
|
|
Status = NtQueryInformationFile( h,
|
|
&IoStatus,
|
|
FileName,
|
|
xbuf.SizeOf(),
|
|
FileNameInformation );
|
|
|
|
Win4Assert( STATUS_PENDING != Status );
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
Status = IoStatus.Status;
|
|
|
|
if ( NT_SUCCESS( Status ) )
|
|
{
|
|
ULONG Length = FileName->FileNameLength;
|
|
cc = 2 + Length/sizeof(WCHAR);
|
|
|
|
//
|
|
// Major cheating here. The FileNameLength field precedes
|
|
// the filename itself, and is juuuust big enough to hold
|
|
// the two character <drive>: preface.
|
|
//
|
|
|
|
Win4Assert( xbuf.Get() + sizeof(WCHAR)*2 == (BYTE *)&FileName->FileName );
|
|
WCHAR * pwszPath = (WCHAR *)xbuf.Get();
|
|
|
|
pwszPath[ 0 ] = (WCHAR) volumeId;
|
|
pwszPath[ 1 ] = L':';
|
|
|
|
funnyPath.SetPath( pwszPath, cc );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "translated fileid %#I64x to path '%ws'\n",
|
|
fileId, funnyPath.GetPath() ));
|
|
|
|
break;
|
|
}
|
|
else if ( STATUS_BUFFER_OVERFLOW == Status )
|
|
{
|
|
xbuf.SetSize( xbuf.SizeOf() * 2 );
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN, "unable to query filenameinfo: %#x\n",
|
|
Status ));
|
|
THROW( CException( Status ) );
|
|
}
|
|
} while (TRUE);
|
|
}
|
|
else
|
|
{
|
|
// STATUS_INVALID_PARAMETER means the file wasn't found.
|
|
|
|
if ( ( !IsSharingViolation( Status ) ) &&
|
|
( STATUS_INVALID_PARAMETER != Status ) &&
|
|
( STATUS_ACCESS_DENIED != Status ) &&
|
|
( STATUS_SHARING_VIOLATION != Status ) &&
|
|
( STATUS_IO_REPARSE_TAG_NOT_HANDLED != Status ) &&
|
|
( STATUS_DELETE_PENDING != Status ) &&
|
|
( STATUS_OBJECT_NAME_NOT_FOUND != Status ) &&
|
|
( STATUS_OBJECT_NAME_INVALID != Status) &&
|
|
( STATUS_OBJECT_PATH_NOT_FOUND != Status) )
|
|
{
|
|
if ( ( STATUS_VOLUME_DISMOUNTED != Status ) &&
|
|
( STATUS_NO_MEDIA_IN_DEVICE != Status ) &&
|
|
( STATUS_UNRECOGNIZED_VOLUME != Status ) &&
|
|
( STATUS_INSUFFICIENT_RESOURCES != Status ) )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "unable to open by id: %#x\n", Status ));
|
|
|
|
#if CIDBG == 1
|
|
ciDebugOut(( DEB_WARN, "error %#x, can't get path for %#I64\n",
|
|
Status, fileId ));
|
|
|
|
char acTemp[ 200 ];
|
|
sprintf( acTemp,
|
|
"New error %#x from FileIdToPath. Call DLee",
|
|
Status );
|
|
Win4AssertEx( __FILE__, __LINE__, acTemp );
|
|
#endif
|
|
}
|
|
|
|
THROW( CException( Status ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_WARN, "no volume match for volume %#x\n", volumeId ));
|
|
}
|
|
|
|
return cc;
|
|
} //FileIdToPath
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::UsnRecordToPathUsingParentId
|
|
//
|
|
// Synopsis: Converts an usn record to path via the parent fileid
|
|
// and checks if path is in scope
|
|
//
|
|
// Arguments: [fileId] -- Fileid of wid
|
|
// [pUsnVolume] -- Volume
|
|
// [lowerFunnyPath] -- Path returned here
|
|
// [fPathInScope] -- Scope info returned here
|
|
// [widParent] -- Parent wid returned here
|
|
// [fParentMayNotBeIndexed] -- TRUE if the parent directory may
|
|
// have the not-indexed attribute, so it's
|
|
// not in the fileid map.
|
|
//
|
|
// History: 23-Jun-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::UsnRecordToPathUsingParentId( USN_RECORD *pUsnRec,
|
|
CUsnVolume *pUsnVolume,
|
|
CLowerFunnyPath & lowerFunnyPath,
|
|
BOOL &fPathInScope,
|
|
WORKID & widParent,
|
|
BOOL fParentMayNotBeIndexed )
|
|
{
|
|
//
|
|
// Workid of parent will be widInvalid for files in root directory
|
|
//
|
|
|
|
widParent = widInvalid;
|
|
fPathInScope = FALSE;
|
|
|
|
WCHAR *pwszFilename = (WCHAR *) (((BYTE *)pUsnRec) + pUsnRec->FileNameOffset);
|
|
unsigned cbFileNameLen = pUsnRec->FileNameLength;
|
|
|
|
BOOL fAddParent = FALSE;
|
|
CLowerFunnyPath parentPath;
|
|
|
|
if ( pUsnRec->ParentFileReferenceNumber == pUsnVolume->RootFileId() )
|
|
{
|
|
//
|
|
// File is in root directory, so build path by prefixing drive letter
|
|
//
|
|
WCHAR awc[3];
|
|
awc[0] = pUsnVolume->DriveLetter();
|
|
awc[1] = L':';
|
|
awc[2] = L'\\';
|
|
|
|
lowerFunnyPath.SetPath( awc, 3 );
|
|
|
|
// The root is named "x:\." (the filename is ".")
|
|
|
|
if ( pUsnRec->FileReferenceNumber != pUsnRec->ParentFileReferenceNumber )
|
|
lowerFunnyPath.AppendPath( pwszFilename, cbFileNameLen/sizeof(WCHAR) );
|
|
}
|
|
else
|
|
{
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
//
|
|
// Build up the full path by prefixing parent directory
|
|
//
|
|
|
|
widParent = _fileIdMap.LokFind( pUsnRec->ParentFileReferenceNumber,
|
|
pUsnVolume->VolumeId() );
|
|
|
|
if ( widParent == widInvalid )
|
|
{
|
|
if ( fParentMayNotBeIndexed )
|
|
{
|
|
//
|
|
// See if the parent directory is marked as non-indexed.
|
|
// If so proceed with the add, otherwise bail.
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE, "doing an expensive open!\n" ));
|
|
|
|
unsigned cc = FileIdToPath( pUsnRec->ParentFileReferenceNumber,
|
|
pUsnVolume->VolumeId(),
|
|
lowerFunnyPath );
|
|
if ( 0 == cc )
|
|
return;
|
|
|
|
DWORD dw = GetFileAttributes( lowerFunnyPath.GetActualPath() );
|
|
|
|
if ( 0xffffffff == dw )
|
|
return;
|
|
|
|
if ( 0 == ( dw & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ) )
|
|
return;
|
|
|
|
fAddParent = TRUE;
|
|
parentPath = lowerFunnyPath;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Unknown parent, which means that this file is out of
|
|
// scope or hasn't been scanned yet, hence do nothing
|
|
//
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned cwcPathLen = _strings.Find( widParent, lowerFunnyPath );
|
|
|
|
if ( 0 == cwcPathLen )
|
|
THROW( CException( STATUS_INVALID_PARAMETER ) );
|
|
|
|
Win4Assert( cwcPathLen == wcslen(lowerFunnyPath.GetActualPath()) );
|
|
}
|
|
}
|
|
|
|
lowerFunnyPath.AppendBackSlash();
|
|
lowerFunnyPath.AppendPath( pwszFilename, cbFileNameLen/sizeof(WCHAR) );
|
|
}
|
|
|
|
//
|
|
// Make sure the path is being indexed
|
|
//
|
|
|
|
if ( !_usnMgr.IsPathIndexed( pUsnVolume, lowerFunnyPath ) )
|
|
return;
|
|
|
|
//
|
|
// Don't filter catalog files and files in ignored scopes
|
|
//
|
|
|
|
if ( ! IsEligibleForFiltering( lowerFunnyPath ) )
|
|
return;
|
|
|
|
if ( fAddParent )
|
|
{
|
|
BOOL fNew;
|
|
widParent = PathToWorkId( parentPath,
|
|
TRUE,
|
|
fNew,
|
|
0,
|
|
eUsnsArray,
|
|
pUsnRec->ParentFileReferenceNumber,
|
|
widInvalid, // "go figure"
|
|
FALSE );
|
|
}
|
|
|
|
fPathInScope = TRUE;
|
|
} //UsnRecordToPathUsingParentId
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::LokMarkForDeletion
|
|
//
|
|
// Synopsis: Mark the wid for deletion. Assumes cicat mutex is held.
|
|
//
|
|
// Arguments: [fileId] -- File id
|
|
// [wid] -- Workid
|
|
//
|
|
// History: 28-Jul-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::LokMarkForDeletion( FILEID fileId,
|
|
WORKID wid )
|
|
{
|
|
Win4Assert( _mutex.IsHeld() );
|
|
ciDebugOut(( DEB_ITRACE, "delete usn %#I64x wid 0x%x\n", fileId, wid ));
|
|
|
|
_fileIdMap.LokDelete( fileId, wid );
|
|
_strings.LokDelete( 0, wid, TRUE, TRUE );
|
|
} //LokMarkForDeletion
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::InitUsnTreeScan
|
|
//
|
|
// Synopsis: Initializes the entry in notification manager that a usn
|
|
// for usn tree traversal is starting.
|
|
//
|
|
// Arguments: [pwszPath] -- Scope
|
|
//
|
|
// History: 28-Jul-97 SitaramR Created
|
|
//
|
|
// Notes: PwszPath may not be in notifymgr because it may have been removed
|
|
// by the time we get here.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::InitUsnTreeScan( WCHAR const *pwszPath )
|
|
{
|
|
for ( CCiNotifyIter iter(_notify);
|
|
!iter.AtEnd();
|
|
iter.Advance() )
|
|
{
|
|
if ( AreIdenticalPaths( iter.Get(), pwszPath ) )
|
|
{
|
|
iter.InitUsnTreeScan();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SetUsnTreeScanComplete
|
|
//
|
|
// Synopsis: Writes usn as the restart usn for the given scope
|
|
//
|
|
// Arguments: [pwszPath] -- Scope
|
|
// [usn] -- Restart usn
|
|
//
|
|
// History: 27-Jun-97 SitaramR Created
|
|
//
|
|
// Notes: PwszPath may not be in notifymgr because it may have been removed
|
|
// by the time we get here.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::SetUsnTreeScanComplete( WCHAR const *pwszPath, USN usn )
|
|
{
|
|
for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() )
|
|
{
|
|
if ( AreIdenticalPaths( iter.Get(), pwszPath ) )
|
|
{
|
|
iter.SetUsnTreeScanComplete( usn );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::SetTreeScanComplete
|
|
//
|
|
// Synopsis: Indicates end of non-USN volume tree scan. Mostly for
|
|
// updating Volume Id info.
|
|
//
|
|
// Arguments: [pwszPath] -- Scope
|
|
//
|
|
// History: 13-Apr-1998 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::SetTreeScanComplete( WCHAR const *pwszPath )
|
|
{
|
|
for ( CCiNotifyIter iter(_notify); !iter.AtEnd(); iter.Advance() )
|
|
{
|
|
if ( AreIdenticalPaths( iter.Get(), pwszPath ) )
|
|
{
|
|
iter.SetTreeScanComplete();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::CheckUsnTreeScan
|
|
//
|
|
// Synopsis: Checks if Usn tree scan has been initialized
|
|
//
|
|
// Arguments: [pwszPath] -- Scope
|
|
//
|
|
// History: 8-Sep-97 SitaramR Created
|
|
//
|
|
// Notes: PwszPath may not be in notifymgr because it may have been removed
|
|
// by the time we get here.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#if CIDBG==1
|
|
|
|
void CiCat::CheckUsnTreeScan( WCHAR const *pwszPath )
|
|
{
|
|
for ( CCiNotifyIter iter(_notify);
|
|
!iter.AtEnd();
|
|
iter.Advance() )
|
|
{
|
|
if ( AreIdenticalPaths( iter.Get(), pwszPath ) )
|
|
{
|
|
// This assert is either bogus or hit in cases we don't understand.
|
|
// When there is time, add logging code to see what's up.
|
|
|
|
// Win4Assert( iter.FUsnTreeScan() );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DebugPrintWidInfo
|
|
//
|
|
// Synopsis: Debug prints wid info
|
|
//
|
|
// Arguments: [wid] -- Wid to print
|
|
//
|
|
// History: 05-Jul-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#if CIDBG == 1
|
|
void CiCat::DebugPrintWidInfo( WORKID wid )
|
|
{
|
|
XGrowable<WCHAR> xPath;
|
|
unsigned ccSize;
|
|
|
|
{
|
|
CLock lock( _mutex );
|
|
ccSize = _strings.Find( wid, xPath );
|
|
}
|
|
|
|
if ( ccSize == 0 )
|
|
xPath[0] = 0;
|
|
|
|
PROPVARIANT propVar;
|
|
_propstoremgr.ReadPrimaryProperty( wid, pidFileIndex, propVar );
|
|
FILEID fileId = propVar.uhVal.QuadPart;
|
|
|
|
_propstoremgr.ReadPrimaryProperty( wid, pidVolumeId, propVar );
|
|
VOLUMEID volumeId = propVar.ulVal;
|
|
|
|
_propstoremgr.ReadPrimaryProperty( wid, pidParentWorkId, propVar );
|
|
WORKID widParent = propVar.ulVal;
|
|
|
|
ciDebugOut(( DEB_ERROR,
|
|
" Wid %#x, fileid %#I64x, volumeId %#x, path %ws, widParent %#x\n",
|
|
wid,
|
|
fileId,
|
|
volumeId,
|
|
xPath.Get(),
|
|
widParent ));
|
|
}
|
|
#endif
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::IsUNCName
|
|
//
|
|
// Synopsis: Determines if a path is a UNC
|
|
//
|
|
// Arguments: [pwszVal] -- The path to check
|
|
//
|
|
// History: ? srikants Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::IsUNCName( WCHAR const * pwszVal )
|
|
{
|
|
Win4Assert( 0 != pwszVal );
|
|
CPathParser parser(pwszVal);
|
|
return parser.IsUNCName();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AddShadowScopes, private
|
|
//
|
|
// Synopsis: Re-adds shadow (virtual) scopes to registry.
|
|
//
|
|
// History: 12-Oct-1997 KyleP Created
|
|
//
|
|
// Notes: This code handles the case where the registry got wiped,
|
|
// possibly by an upgrade, but the catalog data still exists.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::AddShadowScopes()
|
|
{
|
|
//
|
|
// Enumerate through the scope table, looking for entries that map
|
|
// into the virtual namespace.
|
|
//
|
|
|
|
WCHAR wcsPRoot[MAX_PATH+1];
|
|
unsigned ccPRoot;
|
|
|
|
unsigned iBmk = 0;
|
|
|
|
for ( ccPRoot = sizeof(wcsPRoot)/sizeof(WCHAR);
|
|
_scopeTable.Enumerate( wcsPRoot, ccPRoot, iBmk );
|
|
ccPRoot = sizeof(wcsPRoot)/sizeof(WCHAR) )
|
|
{
|
|
if ( _strings.DoesPhysicalRootExist( wcsPRoot ) )
|
|
{
|
|
ciDebugOut(( DEB_FORCE, "Refresh %ws\n", wcsPRoot ));
|
|
_scopeTable.RefreshShadow( wcsPRoot, _xScopesKey.Get() + SKIP_TO_LM );
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Class: CUserPropCallback
|
|
//
|
|
// Purpose: Helper class for CiCat::RecoverUserProperties. Reads
|
|
// registry entries describing cached properties and adds them
|
|
// to the property store.
|
|
//
|
|
// History: 07-Nov-97 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class CUserPropCallback : public CRegCallBack
|
|
{
|
|
public:
|
|
|
|
CUserPropCallback( CiCat & cat, ULONG_PTR ulToken, DWORD dwStoreLevel )
|
|
: _cat( cat ),
|
|
_ulToken( ulToken ),
|
|
_dwStoreLevel( dwStoreLevel )
|
|
{
|
|
}
|
|
|
|
NTSTATUS CallBack( WCHAR *pValueName, ULONG uValueType,
|
|
VOID *pValueData, ULONG uValueLength )
|
|
{
|
|
CParseRegistryProperty ParseProp( pValueName,
|
|
uValueType,
|
|
pValueData,
|
|
uValueLength );
|
|
|
|
if ( ParseProp.IsOk() )
|
|
{
|
|
PROPID pid = _cat.PropertyToPropId( ParseProp.GetFPS(), TRUE );
|
|
|
|
if ( pidInvalid == pid )
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
// Silently fail attempts to monkey with built-in properties.
|
|
|
|
if ( pidPath == pid ||
|
|
pidLastSeenTime == pid ||
|
|
pidAttrib == pid ||
|
|
pidVirtualPath == pid ||
|
|
pidSecurity == pid ||
|
|
pidParentWorkId == pid ||
|
|
pidSecondaryStorage == pid ||
|
|
pidFileIndex == pid ||
|
|
pidVolumeId == pid ||
|
|
pidSize == pid ||
|
|
pidWriteTime == pid )
|
|
return STATUS_SUCCESS;
|
|
|
|
if ( _dwStoreLevel == ParseProp.StorageLevel() )
|
|
_cat._propstoremgr.Setup( pid,
|
|
ParseProp.Type(),
|
|
ParseProp.Size(),
|
|
_ulToken,
|
|
ParseProp.IsModifiable(),
|
|
_dwStoreLevel );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
private:
|
|
|
|
CiCat & _cat;
|
|
ULONG_PTR _ulToken;
|
|
DWORD _dwStoreLevel;
|
|
};
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RecoverUserProperties, private
|
|
//
|
|
// Synopsis: Re-adds user-defined properties to property cache
|
|
//
|
|
// Arguments: [ulToken] -- Token for open property metadata transaction
|
|
// [dwStoreLevel] -- Which storage level are we operating on?
|
|
//
|
|
// History: 07-Nov-1997 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RecoverUserProperties( ULONG_PTR ulToken, DWORD dwStoreLevel )
|
|
{
|
|
TRY
|
|
{
|
|
//
|
|
// Iterate over all the user-defined properties
|
|
//
|
|
|
|
XArray<WCHAR> xPropKey;
|
|
BuildRegistryPropertiesKey( xPropKey, GetCatalogName() );
|
|
|
|
CRegAccess regProps( RTL_REGISTRY_ABSOLUTE, xPropKey.Get() );
|
|
|
|
CUserPropCallback PropCallback( *this, ulToken, dwStoreLevel );
|
|
|
|
regProps.EnumerateValues( 0, PropCallback );
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::RecoverUserProperties: caught 0x%x\n", e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::DeleteUserProperty, private
|
|
//
|
|
// Synopsis: Removes user property definition for registry
|
|
//
|
|
// Arguments: [fps] -- Property to remove
|
|
//
|
|
// History: 11-Nov-1997 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::DeleteUserProperty( CFullPropSpec const & fps )
|
|
{
|
|
TRY
|
|
{
|
|
XArray<WCHAR> xPropKey;
|
|
BuildRegistryPropertiesKey( xPropKey, GetCatalogName() );
|
|
|
|
HKEY hkey;
|
|
DWORD dwError = RegOpenKey( HKEY_LOCAL_MACHINE, xPropKey.Get() + SKIP_TO_LM, &hkey );
|
|
|
|
if ( ERROR_SUCCESS != dwError )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, xPropKey.Get() ));
|
|
}
|
|
else
|
|
{
|
|
SRegKey xkey( hkey );
|
|
|
|
CBuildRegistryProperty PropBuild( fps, 0, 0 ); // Note type and size don't matter.
|
|
|
|
RegDeleteValue( hkey, PropBuild.GetValue() );
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::DeleteUserProperty: caught 0x%x\n", e.GetErrorCode() ));
|
|
}
|
|
END_CATCH
|
|
} //DeleteUserProperty
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RenameFile, public
|
|
//
|
|
// Synopsis: Renames a file
|
|
//
|
|
// Arguments: [lcaseFunnyOldName] -- The old file name
|
|
// [lcaseFunnyNewName] -- The new file name
|
|
// [ulFileAttrib] -- The new file attributes
|
|
// [volumeId] -- The volume id of the file
|
|
// [fileId] -- The file id of the file or fileIdInvalid
|
|
// if not a USN volume
|
|
//
|
|
// History: 4-Mar-1998 dlee Moved from cicat.hxx
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RenameFile(
|
|
const CLowerFunnyPath & lcaseFunnyOldName,
|
|
const CLowerFunnyPath & lcaseFunnyNewName,
|
|
ULONG ulFileAttrib,
|
|
VOLUMEID volumeId,
|
|
FILEID fileId,
|
|
WORKID widParent )
|
|
{
|
|
CLock lock( _mutex );
|
|
|
|
if ( lcaseFunnyOldName.IsRemote() )
|
|
{
|
|
CImpersonateSystem impersonate;
|
|
RenameFileInternal(lcaseFunnyOldName,
|
|
lcaseFunnyNewName,
|
|
ulFileAttrib,
|
|
volumeId,
|
|
fileId,
|
|
widParent);
|
|
}
|
|
else
|
|
{
|
|
RenameFileInternal(lcaseFunnyOldName,
|
|
lcaseFunnyNewName,
|
|
ulFileAttrib,
|
|
volumeId,
|
|
fileId,
|
|
widParent);
|
|
}
|
|
|
|
} //RenameFile
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::RenameFileInternal, public
|
|
//
|
|
// Synopsis: Renames a file
|
|
//
|
|
// Arguments: [lcaseFunnyOldName] -- The old file name
|
|
// [lcaseFunnyNewName] -- The new file name
|
|
// [ulFileAttrib] -- The new file attributes
|
|
// [volumeId] -- The volume id of the file
|
|
// [fileId] -- The file id of the file or fileIdInvalid
|
|
// if not a USN volume
|
|
//
|
|
// History: 4-Mar-1998 dlee Moved from cicat.hxx
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RenameFileInternal(
|
|
const CLowerFunnyPath & lcaseFunnyOldName,
|
|
const CLowerFunnyPath & lcaseFunnyNewName,
|
|
ULONG ulFileAttrib,
|
|
VOLUMEID volumeId,
|
|
FILEID fileId,
|
|
WORKID widParent )
|
|
{
|
|
BOOL fUsnVolume = IsOnUsnVolume( lcaseFunnyOldName.GetActualPath() );
|
|
WORKID wid;
|
|
|
|
if ( fUsnVolume )
|
|
if ( fileIdInvalid == fileId )
|
|
{
|
|
// Since the old file has been deleted, use the new filename,
|
|
// which should have the same fileId
|
|
wid = LokLookupWid( lcaseFunnyNewName, fileId);
|
|
if ( widInvalid == wid )
|
|
{
|
|
// Could not get a valid wid for the new file. Looks like
|
|
// it has been deleted. Abort !!!
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wid = LokLookupWid( lcaseFunnyOldName, fileId);
|
|
}
|
|
else
|
|
wid = _strings.LokFind( lcaseFunnyOldName.GetActualPath() );
|
|
|
|
ciDebugOut(( DEB_ITRACE, "renamefile %#I64x '%ws' to '%ws'\n",
|
|
fileId, lcaseFunnyOldName.GetActualPath(), lcaseFunnyNewName.GetActualPath() ));
|
|
|
|
_strings.LokRenameFile( lcaseFunnyOldName.GetActualPath(),
|
|
lcaseFunnyNewName.GetActualPath(),
|
|
wid,
|
|
ulFileAttrib,
|
|
volumeId,
|
|
widParent );
|
|
} //RenameFile
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::HasChanged, public
|
|
//
|
|
// Returns: TRUE if [ft] > last filter time
|
|
//
|
|
// Arguments: [wid] -- WorkId
|
|
// [ft] -- Filetime
|
|
//
|
|
// History: 08-Apr-1998 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::HasChanged( WORKID wid, FILETIME const & ft )
|
|
{
|
|
PROPVARIANT propVar;
|
|
|
|
if ( !_propstoremgr.ReadPrimaryProperty( wid, pidLastSeenTime, propVar ) ||
|
|
propVar.vt != VT_FILETIME )
|
|
RtlZeroMemory( &propVar.filetime, sizeof(propVar.filetime) );
|
|
|
|
return ( *(ULONGLONG *)&ft > *(ULONGLONG *)&propVar.filetime );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CScopeEntry::CScopeEntry, public
|
|
//
|
|
// Synopsis: Ctor.
|
|
//
|
|
// Arguments: [pwszScope] -- scope entry to init CScopeEntry
|
|
//
|
|
// Notes: If the passed-in scope contains special chars,
|
|
// only scope preceeding the special char is stored.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CScopeEntry::CScopeEntry(WCHAR const * pwszScope)
|
|
: _fContainsSpecialChar(FALSE)
|
|
{
|
|
Win4Assert( 0 != pwszScope );
|
|
|
|
ULONG scopeLen = wcslen( pwszScope );
|
|
|
|
//
|
|
// Terminate the path with a trailing backslash if not already terminated.
|
|
//
|
|
|
|
_xwcsPath.SetSize( scopeLen + 2 );
|
|
RtlCopyMemory( _xwcsPath.Get(), pwszScope, (scopeLen+1) * sizeof(WCHAR) );
|
|
TerminateWithBackSlash( _xwcsPath.Get(), scopeLen );
|
|
|
|
//
|
|
// truncate paths with special chars.
|
|
//
|
|
|
|
WCHAR * pwszSpecial = GetSpecialCharLocation();
|
|
|
|
if ( pwszSpecial )
|
|
{
|
|
*pwszSpecial = L'\0';
|
|
|
|
while ( pwszSpecial >= _xwcsPath.Get() && L'\\' != *pwszSpecial )
|
|
{
|
|
pwszSpecial--;
|
|
}
|
|
|
|
Win4Assert( L'\\' == *pwszSpecial );
|
|
|
|
//
|
|
// terminate substring.
|
|
//
|
|
*(pwszSpecial+1) = L'\0';
|
|
|
|
_fContainsSpecialChar = TRUE;
|
|
|
|
ciDebugOut(( DEB_ITRACE, "CScopeEntry: %ws\n", _xwcsPath.Get() ));
|
|
}
|
|
|
|
_cclen = wcslen( _xwcsPath.Get() );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CScopeEntry::SetToParentDirectory, public
|
|
//
|
|
// Synopsis: truncates current path to its parent.
|
|
//
|
|
// Arguments: none.
|
|
//
|
|
// returns: TRUE if path was set to parent, FALSE if root was reached.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CScopeEntry::SetToParentDirectory(void)
|
|
{
|
|
|
|
Win4Assert( _fContainsSpecialChar );
|
|
|
|
if ( IsRoot() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Win4Assert( L'\\' == _xwcsPath[_cclen-1] );
|
|
|
|
_xwcsPath[_cclen-1] = L'\0'; // skip last backslash
|
|
|
|
WCHAR * pwszTemp = wcsrchr( _xwcsPath.Get(), L'\\' );
|
|
|
|
*(pwszTemp+1) = L'\0';
|
|
|
|
_cclen = wcslen( _xwcsPath.Get() );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScopeEntry::IsRoot, Private
|
|
//
|
|
// Synopsis: finds if current path is root path
|
|
//
|
|
// Arguments: none.
|
|
//
|
|
// Returns: TRUE if Path is root path, False otherwise.
|
|
//
|
|
// History: 3-30-98 mohamedn created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CScopeEntry::IsRoot(void)
|
|
{
|
|
Win4Assert( _fContainsSpecialChar );
|
|
|
|
unsigned weight = 0;
|
|
|
|
if ( _cclen > 3 && _xwcsPath[1] == L':' )
|
|
{
|
|
weight = 3;
|
|
}
|
|
|
|
for ( WCHAR const * pwszTemp = _xwcsPath.Get(); *pwszTemp; pwszTemp++ )
|
|
{
|
|
if ( L'\\' == *pwszTemp )
|
|
{
|
|
weight += 1;
|
|
}
|
|
}
|
|
|
|
if ( weight > 4 )
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CScopeEntry::GetSpecialCharLocation, private
|
|
//
|
|
// Synopsis: Finds location of 1st special char of contained scope.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// returns: pointer to regx wild card (if one found), null otherwise.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
WCHAR * CScopeEntry::GetSpecialCharLocation()
|
|
{
|
|
for ( WCHAR * pwszTemp = _xwcsPath.Get(); *pwszTemp; pwszTemp++ )
|
|
{
|
|
if ( IsSpecialChar(*pwszTemp) )
|
|
return pwszTemp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CScopeEntry::IsSpecialChar, private
|
|
//
|
|
// Synopsis: determines if input char is regex special char
|
|
//
|
|
// Arguments: [c] -- wchar to test
|
|
//
|
|
// returns: TRUE if input wchar is special regex wchar, FALSE otherwise.
|
|
//
|
|
// History: 3-20-98 mohamedn created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CScopeEntry::IsSpecialChar(WCHAR c)
|
|
{
|
|
for ( WCHAR const * pwszTemp = awcSpecialRegex; *pwszTemp; pwszTemp++ )
|
|
{
|
|
if ( c == *pwszTemp )
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Class: XSmartNetApiBuffer
|
|
//
|
|
// Purpose: Frees buffer allocated by the network APIs
|
|
//
|
|
// History: 7/26/00 dlee created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class XSmartNetApiBuffer
|
|
{
|
|
public:
|
|
XSmartNetApiBuffer( CDynLoadNetApi32 & dlNetApi32, void * p ) :
|
|
_dlNetApi32( dlNetApi32 ), _p( p ) {}
|
|
|
|
~XSmartNetApiBuffer()
|
|
{
|
|
_dlNetApi32.NetApiBufferFree( _p );
|
|
}
|
|
|
|
private:
|
|
CDynLoadNetApi32 & _dlNetApi32;
|
|
void * _p;
|
|
};
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::AddShares, public
|
|
//
|
|
// Synopsis: Examines shared resources and adds to fixup list.
|
|
//
|
|
// Arguments: [dlNetApi32] -- Dynamically loaded NetApi32 DLL (for performance)
|
|
//
|
|
// History: 09-Jun-1998 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::AddShares( CDynLoadNetApi32 & dlNetApi32 )
|
|
{
|
|
CDynLoadMpr dlMpr;
|
|
|
|
HANDLE hEnum = (HANDLE)INVALID_HANDLE_VALUE;
|
|
|
|
do
|
|
{
|
|
WCHAR wcsCompName[MAX_COMPUTERNAME_LENGTH+3] = L"\\\\";
|
|
DWORD ccCompName = sizeof(wcsCompName)/sizeof(WCHAR) - 2;
|
|
|
|
if ( !GetComputerName( &wcsCompName[2], &ccCompName ) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %u from GetComputerName\n", GetLastError() ));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open the enumerator
|
|
//
|
|
|
|
NETRESOURCE nr;
|
|
|
|
nr.dwScope = RESOURCE_GLOBALNET;
|
|
nr.dwType = RESOURCETYPE_ANY;
|
|
nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
|
|
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
nr.lpLocalName = 0;
|
|
nr.lpRemoteName = wcsCompName;
|
|
nr.lpComment = 0;
|
|
nr.lpProvider = 0;
|
|
|
|
DWORD dwError = dlMpr.WNetOpenEnumW( RESOURCE_GLOBALNET,
|
|
RESOURCETYPE_ANY,
|
|
0,
|
|
&nr,
|
|
&hEnum);
|
|
|
|
if ( dwError != NO_ERROR )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %u from WNetOpenEnum\n", dwError ));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open key for reg refresh.
|
|
//
|
|
|
|
HKEY hkey = (HKEY)INVALID_HANDLE_VALUE;
|
|
dwError = RegOpenKey( HKEY_LOCAL_MACHINE, _xScopesKey.Get() + SKIP_TO_LM, &hkey );
|
|
|
|
if ( ERROR_SUCCESS != dwError )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, _xScopesKey.Get() ));
|
|
break;
|
|
}
|
|
|
|
SRegKey xKey( hkey );
|
|
|
|
//
|
|
// Enumerate all shares
|
|
//
|
|
|
|
XArray<BYTE> abBuffer( 4 * 1024 );
|
|
DWORD dwResult;
|
|
|
|
do
|
|
{
|
|
NETRESOURCE * pnr = (NETRESOURCE *)abBuffer.GetPointer();
|
|
DWORD cbBuffer = abBuffer.SizeOf();
|
|
DWORD cEntries = 0xFFFFFFFF;
|
|
|
|
dwResult = dlMpr.WNetEnumResourceW( hEnum,
|
|
&cEntries,
|
|
pnr,
|
|
&cbBuffer );
|
|
|
|
if ( NO_ERROR == dwResult )
|
|
{
|
|
for( unsigned i = 0; i < cEntries; i++ )
|
|
{
|
|
//
|
|
// Is this a disk share?
|
|
//
|
|
|
|
if ( pnr[i].dwType == RESOURCETYPE_DISK &&
|
|
pnr[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE )
|
|
{
|
|
//
|
|
// Find the local physical path.
|
|
//
|
|
|
|
BYTE * pbShareInfo;
|
|
|
|
DWORD dwError = dlNetApi32.NetShareGetInfo( &wcsCompName[2], // Server
|
|
pnr[i].lpRemoteName + ccCompName + 3, // Share
|
|
2, // Level 2
|
|
&pbShareInfo ); // Result
|
|
|
|
if ( NO_ERROR == dwError )
|
|
{
|
|
XSmartNetApiBuffer xbuf( dlNetApi32, pbShareInfo );
|
|
|
|
SHARE_INFO_2 * psi = (SHARE_INFO_2 *)pbShareInfo;
|
|
|
|
Win4Assert( STYPE_DISKTREE == psi->shi2_type );
|
|
|
|
//
|
|
// Is the alias already mapped correctly?
|
|
// If it is, then the alias is correctly recorded
|
|
// in the registry. If not, add it.
|
|
//
|
|
|
|
CLowcaseBuf lcase( psi->shi2_path );
|
|
|
|
if ( !_scopeFixup.IsExactMatch( psi->shi2_path,
|
|
pnr[i].lpRemoteName ) )
|
|
{
|
|
if ( _notify.IsInScope( lcase.Get() ) )
|
|
{
|
|
//
|
|
// Add scope to registry.
|
|
//
|
|
|
|
RefreshIfShadowAlias( psi->shi2_path,
|
|
pnr[i].lpRemoteName,
|
|
hkey );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !_notify.IsInScope( lcase.Get() ) )
|
|
{
|
|
//
|
|
// Remove bogus shadow.
|
|
//
|
|
|
|
DeleteIfShadowAlias( psi->shi2_path, pnr[i].lpRemoteName );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while( NO_ERROR == dwResult );
|
|
}
|
|
while ( FALSE );
|
|
|
|
if ( hEnum != (HANDLE)INVALID_HANDLE_VALUE )
|
|
dlMpr.WNetCloseEnum( hEnum );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::SynchShares, public
|
|
//
|
|
// Synopsis: Adds/removes fixups to match net shares
|
|
//
|
|
// History: 15-May-97 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::SynchShares()
|
|
{
|
|
CDynLoadNetApi32 dlNetApi32;
|
|
|
|
//
|
|
// First, enumerate the registry and remove automatically added aliases that
|
|
// No longer exist.
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE, "Reading scope fixups from '%ws'\n", GetScopesKey() ));
|
|
|
|
BOOL fChanged;
|
|
do
|
|
{
|
|
fChanged = FALSE;
|
|
|
|
CRegAccess regScopes( RTL_REGISTRY_ABSOLUTE, GetScopesKey() );
|
|
|
|
CRegistryScopesCallBackRemoveAlias callback( *this, dlNetApi32, !_fAutoAlias );
|
|
|
|
TRY
|
|
{
|
|
regScopes.EnumerateValues( 0, callback );
|
|
fChanged = callback.WasScopeRemoved();
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"Exception 0x%x caught groveling ci registry in SynchShares.\n",
|
|
e.GetErrorCode() ));
|
|
|
|
HandleError( e.GetErrorCode() );
|
|
}
|
|
END_CATCH
|
|
|
|
} while ( fChanged );
|
|
|
|
//
|
|
// Now, enumerate the shares on the machine, and add all the aliases that
|
|
// are not already in the list.
|
|
//
|
|
|
|
if ( _fAutoAlias )
|
|
AddShares( dlNetApi32 );
|
|
}
|
|
|
|
//
|
|
// Local constants
|
|
//
|
|
|
|
WCHAR const wcsShadowAlias[] = L",,41"; // ,,41 --> Shadow alias, Physical, Indexed
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::DeleteIfShadowAlias, private
|
|
//
|
|
// Synopsis: Deletes shadow scope registry entry
|
|
//
|
|
// Arguments: [pwcsScope] -- Scope to delete
|
|
// [pwcsAlias] -- Alias of [pwcsScope]
|
|
//
|
|
// History: 15-May-97 KyleP Created
|
|
//
|
|
// Notes: Only deletes exact matches (as stored by system)
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::DeleteIfShadowAlias( WCHAR const * pwcsScope, WCHAR const * pwcsAlias )
|
|
{
|
|
HKEY hkey = (HKEY)INVALID_HANDLE_VALUE;
|
|
DWORD dwError = RegOpenKey( HKEY_LOCAL_MACHINE, _xScopesKey.Get() + SKIP_TO_LM, &hkey );
|
|
|
|
if ( ERROR_SUCCESS != dwError )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %d opening %ws\n", dwError, _xScopesKey.Get() ));
|
|
}
|
|
|
|
if ( ERROR_SUCCESS == dwError )
|
|
{
|
|
SRegKey xKey( hkey );
|
|
|
|
unsigned ccAlias = wcslen(pwcsAlias);
|
|
unsigned cbAlias = ccAlias * sizeof(WCHAR);
|
|
|
|
//
|
|
// See if this is a shadow entry (flags == 2)
|
|
//
|
|
|
|
WCHAR wcsValueData[MAX_PATH];
|
|
DWORD cbValueData = sizeof(wcsValueData);
|
|
|
|
DWORD dwType;
|
|
|
|
//
|
|
// First, try the most obvious candidate (no '<#>' at end)
|
|
//
|
|
|
|
dwError = RegQueryValueEx( hkey, // Key handle
|
|
pwcsScope, // Name
|
|
0, // Reserved
|
|
&dwType, // Datatype
|
|
(BYTE *)wcsValueData, // Data returned here
|
|
&cbValueData ); // Size of data
|
|
|
|
if ( ERROR_SUCCESS == dwError &&
|
|
REG_SZ == dwType &&
|
|
cbValueData == ( sizeof(wcsShadowAlias) + cbAlias ) &&
|
|
RtlEqualMemory( wcsValueData + cbValueData/sizeof(WCHAR) - sizeof(wcsShadowAlias)/sizeof(WCHAR),
|
|
wcsShadowAlias,
|
|
sizeof(wcsShadowAlias) ) &&
|
|
RtlEqualMemory( wcsValueData, pwcsAlias, cbAlias ) )
|
|
{
|
|
dwError = RegDeleteValue( hkey, pwcsScope );
|
|
|
|
if ( ERROR_SUCCESS != dwError ) {
|
|
ciDebugOut(( DEB_ERROR, "Error %d deleting %ws\n", dwError, pwcsScope ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try it the hard way, via enumeration
|
|
//
|
|
|
|
else
|
|
{
|
|
DWORD dwIndex = 0;
|
|
unsigned ccScope = wcslen(pwcsScope);
|
|
|
|
do
|
|
{
|
|
WCHAR wcsValueName[MAX_PATH+4];
|
|
DWORD ccValueName = sizeof(wcsValueName)/sizeof(WCHAR);
|
|
cbValueData = sizeof(wcsValueData);
|
|
|
|
dwError = RegEnumValue( hkey, // handle of key to query
|
|
dwIndex, // index of value to query
|
|
wcsValueName, // address of buffer for value string
|
|
&ccValueName, // address for size of value buffer
|
|
0, // reserved
|
|
&dwType, // address of buffer for type code
|
|
(BYTE *)wcsValueData, // address of buffer for value data
|
|
&cbValueData ); // address for size of data buffer
|
|
|
|
dwIndex++;
|
|
|
|
if ( ERROR_SUCCESS == dwError && // Call succeeded
|
|
ccValueName == (ccScope + 3) && // It's long enough (path + '<#>')
|
|
wcsValueName[ccScope] == L'<' && // It's a special path
|
|
RtlEqualMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) ) && // To the right scope
|
|
REG_SZ == dwType && // With a string value
|
|
cbValueData == ( sizeof(wcsShadowAlias) + cbAlias ) &&
|
|
RtlEqualMemory( wcsValueData + ccAlias, wcsShadowAlias,
|
|
sizeof(wcsShadowAlias) ) && // That *is* an auto-alias
|
|
RtlEqualMemory( wcsValueData, pwcsAlias, cbAlias ) ) // To the right scope
|
|
{
|
|
dwError = RegDeleteValue( hkey, wcsValueName );
|
|
|
|
if ( ERROR_SUCCESS != dwError ) {
|
|
ciDebugOut(( DEB_ERROR, "Error %d deleting %ws\n", dwError, wcsValueName ));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (ERROR_SUCCESS == dwError );
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::AddShadowAlias, private
|
|
//
|
|
// Synopsis: Adds shadow scope registry entry
|
|
//
|
|
// Arguments: [pwcsScope] -- Scope to add
|
|
// [pwcsAlias] -- Alias
|
|
// [iSlot] -- Empty <#> slot
|
|
// [hkey] -- Registry key to catalog
|
|
//
|
|
// History: 15-May-97 KyleP Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::AddShadowAlias( WCHAR const * pwcsScope,
|
|
WCHAR const * pwcsAlias,
|
|
unsigned iSlot,
|
|
HKEY hkey )
|
|
{
|
|
//
|
|
// Build string: NAME: <alias>,,41
|
|
//
|
|
|
|
XGrowable<WCHAR> xValue;
|
|
|
|
unsigned ccAlias = wcslen(pwcsAlias);
|
|
xValue.SetSize( ccAlias + sizeof(wcsShadowAlias)/sizeof(WCHAR) );
|
|
|
|
RtlCopyMemory( xValue.Get(), pwcsAlias, ccAlias * sizeof(WCHAR) );
|
|
RtlCopyMemory( xValue.Get() + ccAlias, wcsShadowAlias, sizeof(wcsShadowAlias) );
|
|
|
|
WCHAR wcsValueName[MAX_PATH+4];
|
|
unsigned ccScope = wcslen(pwcsScope);
|
|
|
|
if ( ccScope + 4 > sizeof(wcsValueName)/sizeof(WCHAR) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "CiCat::AddShadowAlias: Scope too big (%ws)\n", pwcsScope ));
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) );
|
|
wcsValueName[ccScope] = L'<';
|
|
wcsValueName[ccScope+1] = iSlot + L'0';
|
|
wcsValueName[ccScope+2] = L'>';
|
|
wcsValueName[ccScope+3] = 0;
|
|
|
|
DWORD dwError = RegSetValueEx( hkey, // Key
|
|
wcsValueName, // Value name
|
|
0, // Reserved
|
|
REG_SZ, // Type
|
|
(BYTE *)xValue.Get(), // Data
|
|
ccAlias * sizeof(WCHAR) + sizeof(wcsShadowAlias) ); // Size (in bytes)
|
|
|
|
if ( ERROR_SUCCESS != dwError ) {
|
|
ciDebugOut(( DEB_ERROR, "Error %d writing %ws\n", dwError, pwcsScope ));
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CiCat::RefreshIfShadow, private
|
|
//
|
|
// Synopsis: Refresh shadow scope registry entry (if blank)
|
|
//
|
|
// Arguments: [pwcsScope] -- Scope to refresh
|
|
// [pwcsAlias] -- Alias
|
|
// [hkey] -- Registry key to catalog
|
|
//
|
|
// History: 11-Oct-97 KyleP Created
|
|
//
|
|
// Notes: Only refresh blank (missing) entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CiCat::RefreshIfShadowAlias( WCHAR const * pwcsScope,
|
|
WCHAR const * pwcsAlias,
|
|
HKEY hkey )
|
|
{
|
|
unsigned Empty = 1000; // Just bigger than 9;
|
|
unsigned ccAlias = wcslen(pwcsAlias);
|
|
unsigned cbAlias = ccAlias * sizeof(WCHAR);
|
|
|
|
//
|
|
// See if this is a missing entry
|
|
//
|
|
|
|
WCHAR wcsData[MAX_PATH];
|
|
|
|
DWORD dwType;
|
|
DWORD dwSize = sizeof(wcsData);
|
|
|
|
DWORD dwError = RegQueryValueEx( hkey, // Key handle
|
|
pwcsScope, // Name
|
|
0, // Reserved
|
|
&dwType, // Datatype
|
|
(BYTE *)wcsData, // Data returned here
|
|
&dwSize ); // Size of data
|
|
|
|
if ( ERROR_SUCCESS == dwError &&
|
|
REG_SZ == dwType &&
|
|
dwSize > (ccAlias * sizeof(WCHAR)) &&
|
|
wcsData[ccAlias] == L',' &&
|
|
RtlEqualMemory( pwcsAlias, wcsData, cbAlias ) )
|
|
Empty = 1000;
|
|
else
|
|
{
|
|
WCHAR wcsValueName[MAX_PATH+4];
|
|
unsigned ccScope = wcslen(pwcsScope);
|
|
|
|
RtlCopyMemory( wcsValueName, pwcsScope, ccScope * sizeof(WCHAR) );
|
|
wcsValueName[ccScope] = L'<';
|
|
//wcsValueName[ccScope+1] = L'1';
|
|
wcsValueName[ccScope+2] = L'>';
|
|
wcsValueName[ccScope+3] = 0;
|
|
|
|
for ( unsigned i = 0; i < 10; i++ )
|
|
{
|
|
wcsValueName[ccScope+1] = i + L'0';
|
|
dwSize = sizeof(wcsData);
|
|
|
|
dwError = RegQueryValueEx( hkey, // Key handle
|
|
wcsValueName, // Name
|
|
0, // Reserved
|
|
&dwType, // Datatype
|
|
(BYTE *)wcsData, // Data returned here
|
|
&dwSize ); // Size of data
|
|
|
|
if ( ERROR_FILE_NOT_FOUND == dwError && Empty > 9 )
|
|
Empty = i;
|
|
|
|
if ( ERROR_SUCCESS == dwError &&
|
|
REG_SZ == dwType &&
|
|
dwSize > (ccAlias * sizeof(WCHAR)) &&
|
|
wcsData[ccAlias] == L',' &&
|
|
RtlEqualMemory( pwcsAlias, wcsData, cbAlias ) )
|
|
{
|
|
Empty = 1000;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Empty < 10 )
|
|
AddShadowAlias( pwcsScope, pwcsAlias, Empty, hkey );
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::PropertyRecordToFileId, public
|
|
//
|
|
// Synopsis: Looks up the fileid and volumeid from a property record
|
|
//
|
|
// Arguments: [PropRec] -- The record to use for the reads
|
|
// [fileId] -- Returns the fileid of the file
|
|
// [volumeId] -- Returns the volumeid of the file
|
|
//
|
|
// Returns: TRUE if valid fileid and volumeid values were found
|
|
//
|
|
// History: 2-April-1998 dlee created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CiCat::PropertyRecordToFileId(
|
|
CCompositePropRecord & PropRec,
|
|
FILEID & fileId,
|
|
VOLUMEID & volumeId )
|
|
{
|
|
PROPVARIANT var;
|
|
_propstoremgr.ReadProperty( PropRec, pidFileIndex, var );
|
|
fileId = var.uhVal.QuadPart;
|
|
|
|
if ( ( VT_UI8 == var.vt ) && ( fileIdInvalid != fileId ) )
|
|
{
|
|
_propstoremgr.ReadProperty( PropRec, pidVolumeId, var );
|
|
volumeId = var.ulVal;
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"propertyrecordtofileid, type %#x, volid %#x\n",
|
|
var.vt, volumeId ));
|
|
|
|
return ( ( VT_UI4 == var.vt ) &&
|
|
( CI_VOLID_USN_NOT_ENABLED != volumeId ) );
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE, "propertyrecordtofileid failed!\n" ));
|
|
|
|
return FALSE;
|
|
} //PropertyRecordToFileId
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CiCat::ClearNonStorageProperties, public
|
|
//
|
|
// Synopsis: write VT_EMPTY into the properties in the PropertyStores (
|
|
// except the storage properties, e.g. path, filename, etc.)
|
|
// before a file is reindexed. Thus, if a property value is
|
|
// deleted from the document, the old values will be wiped out.
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
//
|
|
// History: 06-Oct-2000 KitmanH Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CiCat::ClearNonStoragePropertiesForWid( WORKID wid )
|
|
{
|
|
if ( widInvalid != wid )
|
|
{
|
|
XWriteCompositeRecord rec( _propstoremgr, wid );
|
|
_propstoremgr.ClearNonStorageProperties( rec.GetReference() );
|
|
}
|
|
}
|