|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: Strings.cxx
//
// Contents: Strings and hash table used by cicat
//
// History: 17-May-1993 BartoszM Created
// 03-Jan-96 KyleP Integrate with property cache
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <mmstrm.hxx>
#include <cistore.hxx>
#include <prpstmgr.hxx>
#include <propiter.hxx>
#include <propobj.hxx>
#include <pathpars.hxx>
#include <cievtmsg.h>
#include <eventlog.hxx>
#include "cicat.hxx"
#include "usntree.hxx"
#include "strings.hxx"
//+---------------------------------------------------------------------------
//
// Member: CStrings::CStrings, public
//
// Synopsis: Simple half of 2-phase construction
//
// Arguments: [PropStore] -- Property store.
// [cicat] -- Catalog
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
CStrings::CStrings( CPropStoreManager & PropStoreMgr, CiCat & cicat ) : CPersHash( PropStoreMgr, TRUE ), _cicat(cicat) { }
//+---------------------------------------------------------------------------
//
// Member: CStrings::FastInit
//
// Synopsis: Opens persistent hash.
//
// Arguments: [wcsCatDir] -- Catalog directory
// [version] -- Content index version
//
// Returns: TRUE if table was successfully opened.
//
// History: 27-Dec-95 KyleP Created.
// 13-Mar-98 KitmanH Passed in False to CPerHash::FastInit
// to specify that a PersHash is wanted
//
//----------------------------------------------------------------------------
BOOL CStrings::FastInit( CiStorage * pStorage, ULONG version ) { CPersHash::FastInit( pStorage, version, FALSE );
//
// Intialize virtual/physical map.
//
_vmap.Init( pStorage->QueryVirtualScopeList( 0 ) );
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CStrings::Empty
//
// Synopsis: Method to empty out any of the initialized members. This is
// called if corruption is detected and so all resources must
// be released.
//
// History: 3-17-96 srikants Created
//
//----------------------------------------------------------------------------
void CStrings::Empty() { CPersHash::Empty();
_vmap.Empty(); }
//+---------------------------------------------------------------------------
//
// Member: CStrings::LongInit
//
// Synopsis: Initialization that may take a long time
//
// Arguments: [version] - The version of the compiled code.
// [fDirtyShutdown] - Set to TRUE if the previous shutdown was
// dirty.
//
// History: 3-06-96 srikants Created
//
//----------------------------------------------------------------------------
void CStrings::LongInit( ULONG version, BOOL fDirtyShutdown ) { CPersHash::LongInit( version, fDirtyShutdown );
//
// On a dirty shutdown, we should revirtualize because the property store
// may have lost some of the changes made prior to shutdown.
//
if ( !_vmap.IsClean() || fDirtyShutdown ) { ciDebugOut(( DEB_WARN, "Virtual mapping suspect. ReVirtualizing...\n" )); ReVirtualize(); _vmap.MarkClean(); } } //LongInit
//+---------------------------------------------------------------------------
//
// Member: CStrings::ReInit, public
//
// Synopsis: Clears out hash table
//
// Arguments: [version] -- Content index version
//
// Returns: TRUE if table was successfully opened.
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
BOOL CStrings::ReInit ( ULONG version ) { CPersHash::ReInit( version );
return TRUE; } //ReInit
//+-------------------------------------------------------------------------
//
// Member: CStrings::LokAdd, public
//
// Synopsis: Add a new path without updating the hash table
// or the persistent count
//
// Arguments: [pwcPath] -- File path
// [fileId] -- file id for the file if available.
// [fUsnVolume] -- TRUE if the file is from a USN volume
// [widParent] -- widInvalid or the parent wid
// [ulAttrib] -- file attributes
// [pftLastSeenTime] -- file last seen time
//
// History: 19-May-93 BartoszM Created
//
//--------------------------------------------------------------------------
WORKID CStrings::LokAdd( WCHAR const * pwcPath, FILEID fileId, BOOL fUsnVolume, WORKID widParent, ULONG ulAttrib, FILETIME const * pftLastSeenTime ) { Win4Assert( _fFullInit ); Win4Assert( 0 != pwcPath && wcslen(pwcPath) > 0 ); Win4Assert( L':' == pwcPath[1] || (L'\\' == pwcPath[0] && L'\\' == pwcPath[1]) );
//
// Must grow hash table first, as it grovels through property store.
//
if ( _hTable.IsFull() ) GrowHashTable();
//
// Store path in new record.
//
PROPVARIANT var; var.vt = VT_LPWSTR; var.pwszVal = (WCHAR *)pwcPath;
WORKID wid = _PropStoreMgr.WritePropertyInNewRecord( pidPath, *(CStorageVariant const *)(ULONG_PTR)&var );
VOLUMEID volumeId = fUsnVolume ? _cicat.MapPathToVolumeId( pwcPath ) : CI_VOLID_USN_NOT_ENABLED;
ciDebugOut(( DEB_ITRACE, "adding volume %#x %s file wid %#x, '%ws'\n", volumeId, fUsnVolume ? "usn" : "fat", wid, pwcPath ));
if ( !fUsnVolume ) _hTable.Add ( HashFun(pwcPath), wid );
ULONG ulVPathId = _vmap.PhysicalPathToId( pwcPath );
ULONG ulParentWid; if ( widInvalid == widParent) { //
// If the parent is deleted by now, store the parent as widUnused
// instead of widInvalid, in an attempt to avoid confusion.
//
ulParentWid = LokParentWorkId( pwcPath, fUsnVolume );
if ( widInvalid == ulParentWid ) ulParentWid = widUnused; } else ulParentWid = widParent;
//
// Open a composite property record for doing all the writes below
//
XWriteCompositeRecord rec( _PropStoreMgr, wid );
//
// Initialize the SDID to avoid showing the file before it is
// filtered.
//
var.vt = VT_UI4; var.ulVal = sdidInvalid; SCODE sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidSecurity, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
//
// Write the fileindex, and other default ntfs properties for non-ntfs 5.0 wids
//
var.vt = VT_UI8; var.uhVal.QuadPart = fileId;
sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidFileIndex, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
var.vt = VT_UI4; var.ulVal = volumeId; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidVolumeId, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
//
// Write parent wid to prop store
//
var.ulVal = ulParentWid; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidParentWorkId, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
//
// Determine the virtual path.
//
var.ulVal = ulVPathId; var.vt = VT_UI4; sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidVirtualPath, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc)); //ciDebugOut(( DEB_ITRACE, "%ws --> VPath %d\n", pwcPath, var.ulVal ));
//
// Init the last seen time
//
if ( 0 == pftLastSeenTime ) RtlZeroMemory( &var.filetime, sizeof var.filetime ); else var.filetime = *pftLastSeenTime; var.vt = VT_FILETIME; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidLastSeenTime, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
//
// Init the attributes
//
var.vt = VT_UI4; var.ulVal = ulAttrib; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&var ); if (FAILED(sc)) THROW(CException(sc));
rec.Free();
// Update both seen arrays so the new file isn't deleted at the end
// of the scan.
if ( _afSeenScans.Size() > 0 ) SET_SEEN( &_afSeenScans, wid, SEEN_NEW );
if ( _afSeenUsns.Size() > 0 ) SET_SEEN( &_afSeenUsns, wid, SEEN_NEW );
ciDebugOut(( DEB_ITRACE, "lokadd 0x%x, usn volume: %d\n", wid, fUsnVolume ));
return wid; } //LokAdd
//+---------------------------------------------------------------------------
//
// Member: CStrings::LokDelete, public
//
// Synopsis: Delete string from table
//
// Arguments: [pwcPath] -- Path of the document, in lower case
// [wid] -- Workid to remove
// [fDisableDeletionCheck] -- Should we assert that the deleted
// entry must be found ?
// [fUsnVolume] -- TRUE if the file is on a USN volume
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
void CStrings::LokDelete( WCHAR const * pwcPath, WORKID wid, BOOL fDisableDeletionCheck, BOOL fUsnVolume ) { XGrowable<WCHAR> awc; WCHAR const * pwcName = awc.Get(); BOOL fFound = TRUE;
if ( !fUsnVolume ) { if ( 0 != pwcPath ) { AssertLowerCase( pwcPath, 0 ); pwcName = pwcPath; } else { fFound = (Find( wid, awc) > 0); pwcName = awc.Get(); } }
if ( fFound ) { _PropStoreMgr.DeleteRecord( wid ); ciDebugOut(( DEB_ITRACE, "LokDelete 0x%x, usn %d\n", wid, fUsnVolume ));
if ( !fUsnVolume ) { Win4Assert( 0 != pwcName ); _hTable.Remove( HashFun( pwcName ), wid, fDisableDeletionCheck ); } } } //LokDelete
//+---------------------------------------------------------------------------
//
// Member: CStrings::LokRenameFile
//
// Synopsis: Rename old file to new file
//
// Arguments: [pwcsOldFileName] -- Old file name as a funny path or remote path
// [pwcsNewFileName] -- New file name as a funny path or remote path
// [ulFileAttib] -- File attributes of new file
// [volumeId] -- Volume id
//
// History: 20-Mar-96 SitaramR Created
//
//----------------------------------------------------------------------------
void CStrings::LokRenameFile( const WCHAR * pwcsOldFileName, const WCHAR * pwcsNewFileName, WORKID wid, ULONG ulFileAttrib, VOLUMEID volumeId, WORKID widParent ) { Win4Assert( L':' == pwcsNewFileName[1] || (L'\\' == pwcsNewFileName[0] && L'\\' == pwcsNewFileName[1]) );
ciDebugOut(( DEB_FSNOTIFY, "CStrings: Renaming file (%ws) to (%ws)\n", pwcsOldFileName, pwcsNewFileName ));
BOOL fUsnVolume = ( CI_VOLID_USN_NOT_ENABLED != volumeId );
PROPVARIANT propVar;
if ( widInvalid == wid ) { ciDebugOut(( DEB_ITRACE, "adding '%ws' via the rename path\n", pwcsNewFileName )); wid = LokAdd( pwcsNewFileName, fileIdInvalid, fUsnVolume, widParent );
propVar.vt = VT_UI4; propVar.ulVal = ulFileAttrib; SCODE sc = _PropStoreMgr.WritePrimaryProperty( wid, pidAttrib, *(CStorageVariant const *)(ULONG_PTR)&propVar ); if (FAILED(sc)) THROW(CException(sc));
return; }
// Files from USN volumes aren't in this hash table
if ( !fUsnVolume ) { _hTable.Remove( HashFun(pwcsOldFileName), wid, FALSE ); _hTable.Add( HashFun(pwcsNewFileName), wid ); }
if ( widInvalid == widParent ) widParent = LokParentWorkId( pwcsNewFileName, fUsnVolume );
XWriteCompositeRecord rec( _PropStoreMgr, wid );
propVar.vt = VT_LPWSTR; propVar.pwszVal = (WCHAR*)pwcsNewFileName; SCODE sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidPath, *(CStorageVariant const *)(ULONG_PTR)&propVar );
if (FAILED(sc)) THROW(CException(sc));
propVar.vt = VT_UI4; propVar.ulVal = _vmap.PhysicalPathToId( pwcsNewFileName ); sc = _PropStoreMgr.WriteProperty( rec.GetReference(), pidVirtualPath, *(CStorageVariant const *)(ULONG_PTR)&propVar );
if (FAILED(sc)) THROW(CException(sc));
propVar.ulVal = ulFileAttrib; sc = _PropStoreMgr.WritePrimaryProperty( rec.GetReference(), pidAttrib, *(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)); } //LokRenameFile
//+---------------------------------------------------------------------------
//
// Member: CStrings::HashAll, public
//
// Synopsis: Re-hash all strings (after hash table growth)
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
void CStrings::HashAll() { XGrowable<WCHAR> awc;
// Count the number of hash table entries and grow the hash table
{ CPropertyStoreWids iter( _PropStoreMgr ); unsigned cWids = 0;
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); }
//
// Files from USN volumes aren't in the strings table
//
PROPVARIANT var; if ( _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, var ) && ( VT_UI8 == var.vt ) && ( fileIdInvalid != var.uhVal.QuadPart ) ) continue;
//
// It is possible to have a top-level wid in the propstore without
// a pidPath. This can happen if the system crashes in the middle of
// WritePropertyInNewRecord, when a new top-level wid has been created
// but pidPath hasn't yet been written.
//
if ( Find( wid, awc ) > 0 ) cWids++; else _PropStoreMgr.DeleteRecord( wid ); }
GrowToSize( cWids ); }
// Add the wids to the hash table
CPropertyStoreWids iter( _PropStoreMgr );
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping HashAll because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); }
//
// Files from USN volumes aren't in the strings table
//
PROPVARIANT var; if ( _PropStoreMgr.ReadPrimaryProperty( wid, pidFileIndex, var ) && ( VT_UI8 == var.vt ) && ( fileIdInvalid != var.uhVal.QuadPart ) ) continue;
//
// It is possible to have a top-level wid in the propstore without
// a pidPath. This can happen if the system crashes in the middle of
// WritePropertyInNewRecord, when a new top-level wid has been created
// but pidPath hasn't yet been written.
//
if ( Find( wid, awc ) > 0 ) _hTable.Add ( HashFun( awc.Get() ), wid ); else _PropStoreMgr.DeleteRecord( wid ); } } //HashAll
//+---------------------------------------------------------------------------
//
// Member: CStrings::AddVirtualScope, public
//
// Synopsis: Add new virtual/physical path mapping
//
// Arguments: [vroot] -- Virtual path
// [root] -- Physical path
// [fAutomatic] -- TRUE for root tied to Gibraltar
// [eType] -- root type
// [fVRoot] -- TRUE if a vroot, not a vdir
// [fIsIndexed] -- TRUE if should be indexed, FALSE otherwise
//
// Returns: TRUE if a change was made.
//
// History: 05-Feb-96 KyleP Created.
//
//----------------------------------------------------------------------------
BOOL CStrings::AddVirtualScope( WCHAR const * vroot, WCHAR const * root, BOOL fAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fIsIndexed ) { ULONG idNew;
if ( _vmap.Add( vroot, root, fAutomatic, idNew, eType, fVRoot, fIsIndexed ) ) { //
// Log event
//
if ( fVRoot && fIsIndexed ) { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_VROOT_ADDED, 2 );
item.AddArg( vroot ); item.AddArg( root );
eventLog.ReportEvent( item ); }
//
// Add new virtual root to all appropriate objects.
//
#if CIDBG == 1
if ( ciInfoLevel & DEB_ITRACE ) { // only directories should have virtual root identifiers
// of 0xffffffff
CPropertyStoreWids iter( _PropStoreMgr ); PROPVARIANT var; unsigned cb = 0; unsigned cb2 = 0; XGrowable<WCHAR> wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && VT_EMPTY != var.vt && 0xffffffff == var.ulVal && _PropStoreMgr.ReadProperty( wid, pidAttrib, var, 0, &cb2 ) && VT_EMPTY != var.vt && ( 0 == ( var.ulVal & FILE_ATTRIBUTE_DIRECTORY ) ) ) { Find( wid, wcPhysPath );
ciDebugOut(( DEB_ITRACE, "vid 0xffffffff wid 0x%x, path '%ws'\n", wid, wcPhysPath.Get() )); } } } #endif // CIDBG == 1
ULONG idParent = _vmap.Parent( idNew );
ciDebugOut(( DEB_ITRACE, "idParent 0x%x, idnew 0x%x\n", idParent, idNew ));
CStorageVariant varNew; varNew.SetUI4( idNew ); Win4Assert( 0xffffffff != idNew );
CPropertyStoreWids iter( _PropStoreMgr ); XGrowable<WCHAR> wcPhysPath;
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { PROPVARIANT var; unsigned cb = 0;
//
// Check if the vdir should be changed to the new one
//
if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && ( VT_EMPTY == var.vt || var.ulVal == idParent || ( _vmap.IsNonIndexedVDir( var.ulVal ) && idNew != _vmap.GetExcludeParent( var.ulVal ) ) ) ) { unsigned cc = Find( wid, wcPhysPath );
Win4Assert( cc > 0 );
if ( cc > 0 ) { if ( _vmap.IsInPhysicalScope( idNew, wcPhysPath.Get(), cc ) ) { ciDebugOut(( DEB_ITRACE, "add, %.*ws 0x%x --> VPath 0x%x\n", cc, wcPhysPath, var.ulVal, idNew )); SCODE sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } } else { // ciDebugOut(( DEB_ITRACE, "ignoring id %d, isnivd %d\n",
// var.ulVal, _vmap.IsNonIndexedVDir( var.ulVal ) ));
} }
_vmap.MarkClean();
return TRUE; } else return FALSE; } //AddVirtualScope
//+---------------------------------------------------------------------------
//
// Member: CStrings::RemoveVirtualScope, public
//
// Synopsis: Remove virtual/physical path mapping
//
// Arguments: [vroot] -- Virtual path
// [fOnlyIfAutomatic] -- If TRUE, then a manual root will not
// be removed
// [eType] -- type of root
// [fVRoot] -- TRUE if a vroot, FALSE if a vdir
// [fForceVPathFixing] -- forces fixups of removed vpaths
//
// History: 05-Feb-96 KyleP Created.
//
//----------------------------------------------------------------------------
BOOL CStrings::RemoveVirtualScope( WCHAR const * vroot, BOOL fOnlyIfAutomatic, CiVRootTypeEnum eType, BOOL fVRoot, BOOL fForceVPathFixing ) { ULONG idOld, idNew;
if ( _vmap.Remove( vroot, fOnlyIfAutomatic, idOld, idNew, eType, fVRoot ) || fForceVPathFixing ) { //
// Log event
//
if ( fVRoot ) { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_VROOT_REMOVED, 1 );
item.AddArg( vroot );
eventLog.ReportEvent( item ); }
//
// Swap virtual root to all appropriate objects.
//
#if CIDBG == 1
if ( ciInfoLevel & DEB_ITRACE ) { // only directories should have virtual root identifiers
// of 0xffffffff
CPropertyStoreWids iter( _PropStoreMgr ); PROPVARIANT var; unsigned cb = 0; unsigned cb2 = 0; XGrowable<WCHAR> wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && VT_EMPTY != var.vt && 0xffffffff == var.ulVal && _PropStoreMgr.ReadProperty( wid, pidAttrib, var, 0, &cb2 ) && VT_EMPTY != var.vt && ( 0 == ( var.ulVal & FILE_ATTRIBUTE_DIRECTORY ) ) ) { Find( wid, wcPhysPath );
ciDebugOut(( DEB_ITRACE, "vid 0xffffffff wid 0x%x, path '%ws'\n", wid, wcPhysPath.Get() )); } } } #endif // CIDBG == 1
CStorageVariant varNew; varNew.SetUI4( idNew );
CPropertyStoreWids iter( _PropStoreMgr );
BOOL fIsNewExcludeParent = _vmap.IsAnExcludeParent( idNew ); SCODE sc;
XGrowable<WCHAR> wcPhysPath; for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { PROPVARIANT var; unsigned cb = 0;
//
// No existing virtual root, or possibility of replacement
//
if ( _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) && var.vt == VT_UI4 && var.ulVal == idOld ) { // If the new vpath has child non-indexed vdirs, the vdir
// needs to be recomputed from scratch, otherwise, just use
// the new id.
if ( fIsNewExcludeParent ) { BOOL fFound = (Find( wid, wcPhysPath ) > 0);
Win4Assert( fFound );
if ( fFound ) { ULONG idBest = _vmap.PhysicalPathToId( wcPhysPath.Get() );
ciDebugOut(( DEB_ITRACE, "removeA: %ws 0x%x --> 0x%x\n", wcPhysPath, idOld, idBest ));
CStorageVariant varNew; varNew.SetUI4( idBest ); sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } else { #if CIDBG == 1
if ( ciInfoLevel & DEB_ITRACE ) { XGrowable<WCHAR> wcPhysPath; unsigned cc = Find( wid, wcPhysPath ); ciDebugOut(( DEB_ITRACE, "removeB %.*ws 0x%x --> VPath 0x%x\n", cc, wcPhysPath.Get(), idOld, idNew )); } #endif // CIDBG == 1
sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew );
if (FAILED(sc)) THROW(CException(sc)); } } }
_vmap.MarkClean();
return TRUE; } else return FALSE; } //RemoveVirtualScope
//+-------------------------------------------------------------------------
//
// Member: CStrings::FindVirtual, private
//
// Synopsis: Given workid, find virtual path. Version which uses pre-opened
// property record.
//
// Arguments: [PropRec] -- Pre-opened property record.
// [cSkip] -- Count of paths to skip.
// [xBuf] -- String returned here
//
// Returns: 0 if string not found ELSE
// Count of chars in xBuf
//
// Note: xBuf is returned as a NULL terminated string
//
// History: 29-Apr-96 AlanW Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::FindVirtual( CCompositePropRecord & PropRec, unsigned cSkip, XGrowable<WCHAR> & xBuf ) { PROPVARIANT var; unsigned cb = 0; unsigned cc = 0;
if ( _PropStoreMgr.ReadProperty( PropRec, pidVirtualPath, var, 0, &cb ) && VT_UI4 == var.vt ) { //
// Skip the specified number of virtual paths.
//
ULONG id = _vmap.FindNthRemoved( var.ulVal, cSkip );
//
// Were there that many possible paths?
//
if ( 0xFFFFFFFF != id ) { //
// There are two cases for building the path.
//
// \short\vpath
// c:\physical\path
// \vpath\longer\than\ppath
//
// If the virtual path is *longer* than the physical path, then we can just
// copy the full physical path starting at such an offset that the virtual
// path can be blasted on top.
//
// If the virtual path is *shorter* than the physical path, then we need
// to fetch the physical path and shift it left.
//
CVMapDesc const & VDesc = _vmap.GetDesc( id );
// This is either a non-indexed virtual directory
// or the vdesc has just been marked as not in use
// though it was in use at the time of FindNthRemoved above.
// Either way, there is no vpath mapping.
//
if ( !VDesc.IsInUse() ) { return 0; }
// NTRAID#DB-NTBUG9-83796-2000/07/31-dlee handling changed vroots can AV if more changes come later.
// VDesc can go stale or be reused at any time, since
// it's being used with no lock held. We may AV in
// some cases here.
unsigned ccVPath = VDesc.VirtualLength(); unsigned ccPPath = VDesc.PhysicalLength();
if ( ccVPath >= ccPPath ) { unsigned delta = ccVPath - ccPPath; XGrowable<WCHAR> xTemp; unsigned ccIn = Find( PropRec, xTemp );
if ( ccIn > 0 ) { xBuf.SetSize( delta + ccIn + 1 ); RtlCopyMemory( xBuf.Get() + delta, xTemp.Get(), ccIn * sizeof( WCHAR ) );
Win4Assert( ccIn >= ccPPath ); Win4Assert( RtlEqualMemory( xBuf.Get() + delta, VDesc.PhysicalPath(), ccPPath * sizeof(WCHAR) ) );
RtlCopyMemory( xBuf.Get(), VDesc.VirtualPath(), ccVPath * sizeof(WCHAR) );
// Null-terminate
//
Win4Assert( xBuf.Count() > ccIn + delta ); xBuf[cc = ccIn + delta] = 0; } } else { unsigned delta = ccPPath - ccVPath; unsigned ccIn = Find( PropRec, xBuf );
if ( ccIn >= ccPPath ) { RtlMoveMemory( xBuf.Get() + ccVPath, xBuf.Get() + ccPPath, (ccIn - ccPPath) * sizeof(WCHAR) ); RtlCopyMemory( xBuf.Get(), VDesc.VirtualPath(), ccVPath * sizeof(WCHAR) );
//
// Null-terminate
//
Win4Assert( xBuf.Count() > ccIn - delta ); xBuf[cc = ccIn - delta] = 0; } } } } return cc; } //FindVirtual
//+---------------------------------------------------------------------------
//
// Member: CStrings::ReVirtualize, private
//
// Synopsis: Verify or correct all virtual mappings.
//
// History: 14-Feb-96 KyleP Created.
//
//----------------------------------------------------------------------------
void CStrings::ReVirtualize() { //
// Loop through property cache and verify virtual mapping.
//
CPropertyStoreWids iter( _PropStoreMgr ); XGrowable<WCHAR> wcPhysPath;
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { //
// Get path.
//
unsigned cc = Find( wid, wcPhysPath );
//
// Get existing virtual root.
//
PROPVARIANT var; unsigned cb = 0;
if ( cc > 0 && _PropStoreMgr.ReadProperty( wid, pidVirtualPath, var, 0, &cb ) ) { //
// Do we need to change?
//
wcPhysPath[cc] = 0; ULONG idNew = _vmap.PhysicalPathToId( wcPhysPath.Get() );
if ( var.vt == VT_EMPTY || var.ulVal != idNew ) { ciDebugOut(( DEB_ITRACE, "ReVirtualize: %ws --> %u\n", wcPhysPath.Get(), idNew ));
CStorageVariant varNew; varNew.SetUI4( idNew );
SCODE sc = _PropStoreMgr.WriteProperty( wid, pidVirtualPath, varNew ); if (FAILED(sc)) THROW(CException(sc)); } } } } //ReVirtualize
//+-------------------------------------------------------------------------
//
// Member: CStrings::HashFun, private
//
// Synopsis: The hash function used to find strings in _strings[]
//
// Arguments: [str] - the string to perform the hash function on
//
// History: 10-Mar-92 BartoszM Created
//
//--------------------------------------------------------------------------
unsigned CStrings::HashFun( WCHAR const * pc ) { unsigned ulG;
for ( unsigned ulH=0; *pc; pc++) { ulH = (ulH << 4) + (*pc); if (ulG = (ulH & 0xf0000000)) ulH ^= ulG >> 24; ulH &= ~ulG; }
return ulH; }
//+-------------------------------------------------------------------------
//
// Member: CStrings::LokFind
//
// Synopsis: Given string, find workid. This works only for FAT volumes.
//
// Arguments: [buf] - String to locate
//
// Returns: Workid of [buf]
//
// History: 27-Dec-95 KyleP Created.
//
//--------------------------------------------------------------------------
WORKID CStrings::LokFind( WCHAR const * buf ) { Win4Assert( _fFullInit ); Win4Assert( 0 != buf ); Win4Assert( FALSE == _cicat.IsOnUsnVolume( buf ) );
#ifdef DO_STATS
unsigned cSearchLen = 0; #endif // DO_STATUS
CShortWidList list( HashFun(buf), _hTable );
unsigned ccIn = wcslen(buf);
//Win4Assert( ccIn < MAX_PATH );
for ( WORKID wid = list.WorkId(); wid != widInvalid; wid = list.NextWorkId() ) { PROPVARIANT var; XGrowable<WCHAR> wcTemp; //unsigned cc = sizeof(wcTemp) / sizeof(WCHAR);
// don't even try to find paths that are longer than ccIn
// account for quad-word align and null-termination by adding 4
unsigned cc = Find( wid, wcTemp );
#ifdef DO_STATS
cSearchLen++; #endif // DO_STATS
// Win4Assert( cc <= sizeof(wcTemp) / sizeof(WCHAR) );
if ( ccIn == cc && RtlEqualMemory( buf, wcTemp.Get(), cc * sizeof(WCHAR) ) ) { #ifdef DO_STATS
_hTable.UpdateStats( cSearchLen ); #endif // DO_STATS
return wid; } }
return widInvalid; } //LokFind
//+-------------------------------------------------------------------------
//
// Member: CStrings::LokFind
//
// Synopsis: Given string, find workid. This works for both FAT and NTFS.
//
// Arguments: [lcaseFunnyPath] - Path to locate
// [fUsnVolume] - Flag to tell whether it's a USN volume or not
//
// Returns: Workid of [lcaseFunnyPath]
//
// History: 18-Aug-98 VikasMan Created.
//
//--------------------------------------------------------------------------
// inline
WORKID CStrings::LokFind( const CLowerFunnyPath & lcaseFunnyPath, BOOL fUsnVolume ) { return ( fUsnVolume ? _cicat.PathToWorkId ( lcaseFunnyPath, FALSE ) : LokFind( lcaseFunnyPath.GetActualPath() ) ); }
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given workid, find the path string.
//
// Arguments: [wid] -- Workid to locate
// [xBuf] -- String returned here
//
// Returns: 0 if string not found ELSE
// Count of chars in xBuf
//
// Note: xBuf is returned as a NULL terminated string
//
// History: 27-Dec-95 KyleP Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( WORKID wid, XGrowable<WCHAR> & xBuf ) { CCompositePropRecord PropRec( wid, _PropStoreMgr );
return Find( PropRec, xBuf ); } //Find
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given PropRec, find the path string.
//
// Arguments: [PropRec] -- Property Record
// [xBuf] -- String returned here
//
// Returns: 0 if string not found ELSE
// Count of chars in xBuf
//
// Note: xBuf is returned as a NULL terminated string
//
// History: 27-Dec-95 KyleP Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( CCompositePropRecord & PropRec, XGrowable<WCHAR> & xBuf ) { unsigned cc = xBuf.Count();
_Find( PropRec, xBuf.Get(), cc );
if ( cc > xBuf.Count() ) { // Need more space
xBuf.SetSize( cc ); _Find( PropRec, xBuf.Get(), cc );
// Can't go on asking for more space forever !
Win4Assert( cc < xBuf.Count() ); }
// Either we didn't find or if we did, then it is null terminated
Win4Assert( 0 == cc || 0 == xBuf[cc] );
return cc; }
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given workid, find the path string.
//
// Arguments: [wid] -- Workid to locate
// [funnyPath] -- String returned here, as funnyPath
//
// Returns: 0 if string not found ELSE
// Count of actual chars in funnyPath
//
// History: 21-May-98 VikasMan Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( WORKID wid, CFunnyPath & funnyPath ) { CCompositePropRecord PropRec( wid, _PropStoreMgr );
return Find( PropRec, funnyPath ); } //Find
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given workid, find the path string.
//
// Arguments: [wid] -- Workid to locate
// [lcaseFunnyPath] -- String returned here, as lcase funnyPath
//
// Returns: 0 if string not found ELSE
// Count of actual chars in funnyPath
//
// History: 21-May-98 VikasMan Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( WORKID wid, CLowerFunnyPath & lcaseFunnyPath ) { CCompositePropRecord PropRec( wid, _PropStoreMgr );
return Find( PropRec, lcaseFunnyPath ); } //Find
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given PropRec, find the path string.
//
// Arguments: [PropRec] -- Property Record
// [funnyPath] -- String returned here, as funnyPath
//
// Returns: 0 if string not found ELSE
// Count of actual chars in funnyPath
//
// History: 21-May-98 VikasMan Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( CCompositePropRecord & PropRec, CFunnyPath & funnyPath ) { XGrowable<WCHAR, MAX_PATH> xBuf; unsigned cc = Find( PropRec, xBuf ); if ( cc > 0 ) { funnyPath.SetPath( xBuf.Get(), cc ); } return cc; }
//+-------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Given PropRec, find the path string.
//
// Arguments: [PropRec] -- Property Record
// [lcaseFunnyPath] -- String returned here, as lcase funnyPath
//
// Returns: 0 if string not found ELSE
// Count of actual chars in funnyPath
//
// History: 21-May-98 VikasMan Created.
//
//--------------------------------------------------------------------------
unsigned CStrings::Find( CCompositePropRecord & PropRec, CLowerFunnyPath & lcaseFunnyPath ) { XGrowable<WCHAR, MAX_PATH> xBuf; unsigned cc = Find( PropRec, xBuf ); if ( cc > 0 ) { lcaseFunnyPath.SetPath( xBuf.Get(), cc, TRUE ); } return cc; }
//+-------------------------------------------------------------------------
//
// Member: CStrings::_Find, private
//
// Synopsis: Given workid, find string. Version which uses pre-opened
// property record.
//
// Arguments: [PropRec] -- Pre-opened property record.
// [buf] -- String returned here
// [cc] -- On input: size in WCHARs of [buf]. On output,
// size required or 0 if string not found.
//
// History: 03-Apr-96 KyleP Created.
//
//--------------------------------------------------------------------------
void CStrings::_Find( CCompositePropRecord & PropRec, WCHAR * buf, unsigned & cc ) { #if 0
//
// First try to get the path based on file id and volume id.
// This only works for files on USN volumes. Paths from files on USN
// volumes aren't in the property cache.
// Sure, this makes FAT lookups slower, but that's the way it goes.
//
VOLUMEID volumeId; FILEID fileId;
if ( _cicat.PropertyRecordToFileId( PropRec, fileId, volumeId ) ) { // PropertyRecordToFileId doesn't return a fileid without a volumeid
Win4Assert( CI_VOLID_USN_NOT_ENABLED != volumeId );
_cicat.FileIdToPath( fileId, volumeId, buf, cc ); return; } #endif
PROPVARIANT var; const unsigned cbIn = cc * sizeof(WCHAR); unsigned cb = cbIn;
if ( !_PropStoreMgr.ReadProperty( PropRec, pidPath, var, (BYTE *)buf, &cb ) ) cc = 0; else { if ( cb <= cbIn ) { Win4Assert( (buf == var.pwszVal && VT_LPWSTR == var.vt) || VT_EMPTY == var.vt);
//
// Length returned from ReadProperty may be the QWORD-ALIGNED length.
// Must adjust to the real length, which will be sans terminating
// null, and maybe a few bytes more.
//
if ( VT_LPWSTR == var.vt ) { //Win4Assert( 0 == (cb & 7) );
//Win4Assert( cb >= sizeof(LONGLONG) );
if ( cb < sizeof(LONGLONG) ) { cc = cb / sizeof(WCHAR) - 1; // -1 for null
} else { cc = cb / sizeof(WCHAR); cc -= sizeof(LONGLONG)/sizeof(WCHAR);
while ( 0 != buf[cc] ) cc++; }
Win4Assert( 0 == buf[cc] ); Win4Assert( 0 != buf[cc-1] ); } else { buf[0] = 0; cc = 0; } } else { //
// The buffer is not big enough.
//
cc = cb/sizeof(WCHAR); } } } //Find
//+---------------------------------------------------------------------------
//
// Member: CStrings::Find
//
// Synopsis: Get the last seen time for the given wid.
//
// Arguments: [wid] -
// [ftLastSeen] -
//
// History: 3-19-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CStrings::Find( WORKID wid, FILETIME & ftLastSeen ) { PROPVARIANT var; unsigned cb;
BOOL fFound = _PropStoreMgr.ReadPrimaryProperty( wid, pidLastSeenTime, var ); if ( fFound && ( VT_FILETIME == var.vt ) ) ftLastSeen = var.filetime; else RtlZeroMemory( &ftLastSeen, sizeof(ftLastSeen) );
return fFound; } //Find
//+-------------------------------------------------------------------------
//
// Member: CStrings::BeginSeen, public
//
// Synopsis: Begin 'seen' processing.
//
// Arguments: [pwcRoot] -- Root path of the seen processing
// [mutex] -- Cicat mutex
// [eType] -- Seen array type
//
// History: 27-Dec-95 KyleP Created
//
//--------------------------------------------------------------------------
void CStrings::BeginSeen( WCHAR const * pwcsRoot, CMutexSem & mutex, ESeenArrayType eType ) { ciDebugOut(( DEB_ITRACE, "BeginSeen 0x%x\n", eType ));
//
// Return time of last *seen* and update to current time.
//
CDynArrayInPlace<BYTE> *pafSeen; if ( eType == eScansArray ) pafSeen = &_afSeenScans; else pafSeen = &_afSeenUsns;
//
// Set array.
//
CPropertyStoreWids iter( _PropStoreMgr ); WORKID widIgnore = 0;
//
// For doing scope testing.
//
ULONG cwcScope = 0 != pwcsRoot ? wcslen(pwcsRoot) : 0;
CScopeMatch scopeTest( pwcsRoot, cwcScope );
XGrowable<WCHAR> wcsPath;
for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.NextWorkId() ) { CLock lock(mutex); // Protect "seen" array.
if ( _fAbort ) { ciDebugOut(( DEB_WARN, "Stopping BeginSeen because of shutdown\n" )); THROW( CException(STATUS_TOO_LATE) ); }
//
// Ignore any missing entries.
//
for ( ; widIgnore < wid; widIgnore++ ) SET_SEEN( pafSeen, widIgnore, SEEN_IGNORE );
widIgnore++;
unsigned cc = Find( wid, wcsPath ); if ( 0 == cc || scopeTest.IsInScope( wcsPath.Get(), cc ) ) SET_SEEN( pafSeen, wid, SEEN_NOT ); else SET_SEEN( pafSeen, wid, SEEN_YES ); }
CLock lock( mutex ); // Protect "seen" array
for ( ; widIgnore < _PropStoreMgr.MaxWorkId(); widIgnore++ ) SET_SEEN( pafSeen, widIgnore, SEEN_IGNORE );
// At the point EndSeen is called, the size of the seen array must
// be non-zero. if it is not non-zero, set it to be so.
if (0 == pafSeen->Size()) pafSeen->SetSize( 10 );
Win4Assert( pafSeen->Size() > 0 ); } //BeginSeen
//+---------------------------------------------------------------------------
//
// Member: CStrings::LokParentWorkId
//
// Synopsis: Returns parent workid of given file
//
// Arguments: [pwcsFileName] -- File name
//
// History: 23-Jun-97 SitaramR Created
// 01-Sep-97 EmilyB Physical drive roots (c:\) and
// UNC roots (\\emilyb\d) have
// no parent.
//
//----------------------------------------------------------------------------
WORKID CStrings::LokParentWorkId( WCHAR const * pwcsFileName, BOOL fUsnVolume ) { //
// return widInvalid if we're at root of physical drive
//
unsigned cwcEnd = wcslen( pwcsFileName ) - 1 ;
if (cwcEnd < 3) return widInvalid; // physical drive root has no parent
//
// backup from end of filename to last \ to get parent
//
while ( pwcsFileName[cwcEnd] != L'\\' && cwcEnd > 1) cwcEnd--;
CLowerFunnyPath lcaseFunnyParent; lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd );
//
// Find parent's wid
//
WORKID widParent = LokFind( lcaseFunnyParent, fUsnVolume );
if (widInvalid == widParent) // if parent didn't have wid, then create one
{ //
// check if we're at root of UNC path - return widInvalid if so
//
const WCHAR * pwszParent = lcaseFunnyParent.GetActualPath();
if (cwcEnd > 2 && pwszParent[0] == L'\\' && pwszParent[1] == L'\\') { // see if last \ is the 2nd \\ in UNC name
while ( pwszParent[cwcEnd] != L'\\') cwcEnd--; if ( 1 == cwcEnd ) return widInvalid; }
//
// Now we traverse the path from the left and create all the wids
// that are needed. We avoid recursion by starting from left.
//
unsigned cwcParentLength = lcaseFunnyParent.GetActualLength(); BOOL fAllPathsInvalidFromNow = FALSE;
cwcEnd = 2;
// In case it is a remote path, start traversal after the machine name
if ( L'\\' == pwcsFileName[0] && L'\\' == pwcsFileName[1] ) { while ( pwcsFileName[cwcEnd] != L'\\' ) cwcEnd++; cwcEnd++; }
for (; cwcEnd < cwcParentLength; cwcEnd++) { if ( L'\\' == pwcsFileName[cwcEnd] ) { lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd ); if ( fAllPathsInvalidFromNow || widInvalid == LokFind( lcaseFunnyParent, fUsnVolume ) ) { // Since we done't have a wid for this path, it should be
// be a valid assumption that all its children also do
// not have valid wids
fAllPathsInvalidFromNow = TRUE;
_cicat.PathToWorkId ( lcaseFunnyParent, TRUE ); } } }
// Now create the final parent wid
lcaseFunnyParent.SetPath( pwcsFileName, cwcEnd ); widParent = _cicat.PathToWorkId ( lcaseFunnyParent, TRUE ); }
//
// wid can be widInvalid if the scope is no longer indexed or has
// been deleted.
//
// Win4Assert( widInvalid != widParent );
//
return widParent; } //LokParentWorkId
|