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

1652 lines
50 KiB
C++

//+---------------------------------------------------------------------------
//
// 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