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

906 lines
27 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: UPDATE.CXX
//
// Contents: Content Index Update
//
// History: 19-Mar-92 AmyA Created.
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <imprsnat.hxx>
#include <pathpars.hxx>
#include <cifrmcom.hxx>
#include <lcase.hxx>
#include "cicat.hxx"
#include "update.hxx"
//+---------------------------------------------------------------------------
//
// Class: CImpersonatedFindFirst
//
// Purpose: A class that is capable of repeatedly trying to do a find
// first until there is success or is not retryable any more.
// Changes impersonation between attempts.
//
// History: 7-18-96 srikants Created
//
//----------------------------------------------------------------------------
class CImpersonatedFindFirst: public PImpersonatedWorkItem
{
public:
CImpersonatedFindFirst( const CFunnyPath & funnyPath, HANDLE & h )
: PImpersonatedWorkItem( funnyPath.GetActualPath() ),
_funnyPath( funnyPath ),
_h(h)
{
}
BOOL OpenFindFirst( CImpersonateRemoteAccess & remoteAccess );
BOOL DoIt(); // virtual
private:
HANDLE & _h;
const CFunnyPath & _funnyPath;
};
//+---------------------------------------------------------------------------
//
// Member: CImpersonatedFindFirst::OpenFindFirst
//
// Synopsis: Opens the first first handle, impersonating as necessary.
//
// Arguments: [remoteAccess] -
//
// Returns: TRUE if successful; FALSE o/w
//
// History: 7-18-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CImpersonatedFindFirst::OpenFindFirst(
CImpersonateRemoteAccess & remoteAccess )
{
BOOL fSuccess = TRUE;
TRY
{
ImpersonateAndDoWork( remoteAccess );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_ERROR, "OpenFindFirst (%ws) failed with error (0x%X)\n",
_funnyPath.GetPath(), e.GetErrorCode() ));
fSuccess = FALSE;
}
END_CATCH
return fSuccess;
}
//+---------------------------------------------------------------------------
//
// Member: CImpersonatedFindFirst::DoIt
//
// Synopsis: The worker method that does the work in the context of
// impersonation.
//
// Returns: TRUE if successful; FALSE if should be retried; THROWS
// otherwise.
//
// History: 7-18-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CImpersonatedFindFirst::DoIt()
{
//
// Open directory
//
NTSTATUS Status =
CiNtOpenNoThrow( _h,
_funnyPath.GetPath(),
FILE_LIST_DIRECTORY | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
if ( !NT_SUCCESS(Status) )
{
if ( IsRetryableError( Status ) )
return FALSE;
else
{
THROW( CException( Status ) );
}
}
return TRUE;
} //DoIt
//+---------------------------------------------------------------------------
//
// Member: CTraverse::CTraverse
//
// Synopsis: Constructor of the CTraverse class.
//
// Arguments: [fAbort] - A reference to an abort triggering variable.
//
// History: 3-15-96 srikants Split from CUpdate
//
//----------------------------------------------------------------------------
CTraverse::CTraverse( CiCat & cat,
BOOL & fAbort,
BOOL fProcessRoot )
: _cat( cat ),
_fAbort(fAbort),
_fProcessRoot(fProcessRoot),
_cProcessed( 0 ),
_xbBuf( FINDFIRST_SIZE ),
_pCurEntry( 0 ),
_status( STATUS_SUCCESS )
{
}
//+---------------------------------------------------------------------------
//
// Member: CTraverse::DoIt
//
// Synopsis: Traverses the tree from the given root and invokes the
// "ProcessFile" method on all the files found. Also, before
// starting to traverse a directory, it calls the
// "IsEligibleForTraversal()" method on that.
//
// Arguments: [lcaseFunnyRootPath] - The path to traverse.
//
// History: 3-15-96 srikants Split from CUpdate constructor
//
// Notes:
//
//----------------------------------------------------------------------------
void CTraverse::DoIt( const CLowerFunnyPath & lcaseFunnyRootPath )
{
Push( lcaseFunnyRootPath );
if ( _fProcessRoot )
{
Win4Assert( 0 == _pCurEntry );
// Note this is a bit of a hack. Only the attributes field is valid.
_pCurEntry = (CDirEntry *)_xbBuf.GetPointer();
ULONG ulAttrib = 0;
if ( CImpersonateRemoteAccess::IsNetPath( lcaseFunnyRootPath.GetActualPath() ) )
{
CImpersonateRemoteAccess remote( _cat.GetImpersonationTokenCache() );
CImpersonatedGetFileAttr getAttr( lcaseFunnyRootPath );
getAttr.DoWork( remote );
ulAttrib = getAttr.GetAttr();
}
else
{
ulAttrib = GetFileAttributes( lcaseFunnyRootPath.GetPath() );
}
_pCurEntry->SetAttributes( ulAttrib );
//
// If we got an error value for file attributes, then substitute zero
// because we don't want the directory bit to be turned on
//
if ( INVALID_FILE_ATTRIBUTES == _pCurEntry->Attributes() )
_pCurEntry->SetAttributes( 0 );
if ( !ProcessFile( lcaseFunnyRootPath ) )
return;
}
CImpersonateRemoteAccess remote( _cat.GetImpersonationTokenCache() );
for (;;)
{
XPtr<CLowerFunnyPath> xLowerFunnyPath;
if ( !Pop( xLowerFunnyPath ) )
break;
if ( !Traverse(xLowerFunnyPath, remote))
break;
}
} //DoIt
//+---------------------------------------------------------------------------
//
// Member: CTraverse::Traverse
//
// Synopsis: Traverses the specified directory.
//
// Arguments: [dir] -
//
// History: 3-15-96 srikants Split from CUpdate::Traverse
//
//----------------------------------------------------------------------------
BOOL CTraverse::Traverse (XPtr<CLowerFunnyPath> & xLowerFunnyPath, CImpersonateRemoteAccess & remote )
{
if ( !IsEligibleForTraversal( xLowerFunnyPath.GetReference() ) )
{
ciDebugOut (( DEB_ITRACE, "Traverse skipping %ws\n", xLowerFunnyPath->GetActualPath() ));
return TRUE;
}
TraversalIdle();
CFullPath fullPath( xLowerFunnyPath->GetActualPath(), xLowerFunnyPath->GetActualLength() );
ciDebugOut (( DEB_ITRACE, "Traversing %ws\n", fullPath.GetBuf()));
ULONG cSkip = 0;
HANDLE h;
if ( CImpersonateRemoteAccess::IsNetPath( xLowerFunnyPath->GetActualPath() ) )
{
CImpersonatedFindFirst findFirst( fullPath.GetFunnyPath(), h );
if ( !findFirst.OpenFindFirst( remote ) )
return TRUE;
}
else
{
//
// Open directory
//
NTSTATUS Status =
CiNtOpenNoThrow( h,
xLowerFunnyPath->GetPath(),
FILE_LIST_DIRECTORY | SYNCHRONIZE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
if ( !NT_SUCCESS(Status) )
return TRUE;
}
SHandle xHandle(h);
//
// First enumeration
//
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
Status = NtQueryDirectoryFile( h, // File
0, // Event
0, // APC routine
0, // APC context
&IoStatus, // I/O Status
_xbBuf.GetPointer(), // Buffer
_xbBuf.SizeOf(), // Buffer Length
FileBothDirectoryInformation,
0, // Multiple entry
0, // Filename
1 ); // Restart scan
if ( NT_SUCCESS(Status) )
Status = IoStatus.Status;
//
// If there are no more files or we don't have access to any more files
// in this branch of the tree, just continue traversing. Otherwise throw.
//
if ( !NT_SUCCESS(Status) )
{
if ( STATUS_NO_MORE_FILES == Status ||
STATUS_NO_SUCH_FILE == Status ||
STATUS_ACCESS_DENIED == Status )
return TRUE;
THROW( CException( Status ) );
}
_pCurEntry = (CDirEntry *)_xbBuf.GetPointer();
BOOL fMore = TRUE;
ULONG ulScanBackoff = _cat.GetRegParams()->GetScanBackoff();
do
{
//
// Sleep for a while to let other I/O get through if appropriate.
//
// For reference, with 105,000 files on a free build, here are the
// scan times ( for mod of 50 and sleep(200) * ulScanBackoff )
//
// ScanBackoff Time (min:sec)
// ----------- --------------
// 0 8:40
// 1 18:03
// 3 43:11
//
if ( 0 != ulScanBackoff )
{
if ( ulScanBackoff > CI_SCAN_BACKOFF_MAX_RANGE )
{
//
// These values are reserved for future use.
//
}
else if ( ( _cProcessed % 50 ) == 0 )
{
for ( unsigned i = 0; !_fAbort && i < ulScanBackoff; i++ )
Sleep( 100 );
//
// If we're running ahead of filtering, then let's take a break.
// What's the point in running up a big change log for no reason?
//
// Attempt to keep primary and secondary store pages in memory.
// The primary store keeps 1489 records per 64k page and the
// secondary by default keeps 140. By keeping the scan
// thread only a little ahead of the filter thread we
// thrash less.
//
unsigned cLoops = 0;
while ( !_fAbort )
{
CI_STATE State;
State.cbStruct = sizeof State;
SCODE sc = _cat.CiState( State );
if ( FAILED(sc) ||
( (State.cDocuments - State.cSecQDocuments) < 200 &&
( 0 == (State.eState & ( CI_STATE_HIGH_IO |
CI_STATE_LOW_MEMORY |
CI_STATE_USER_ACTIVE ) ) ) ) )
break;
//
// Only call TraverseIdle if filtering has been stalled
// for a long time -- every 60 seconds or so.
//
cLoops++;
if ( 0 == ( cLoops % 30 ) )
TraversalIdle( TRUE ); // TRUE --> Stalled (not scanning)
//
// Sleep for a few of seconds, checking for abort.
//
for ( unsigned i = 0; !_fAbort && i < 3; i++ )
Sleep( 100 );
}
}
}
_cProcessed++;
//
// Get out?
//
if ( _fAbort )
{
ciDebugOut(( DEB_ITRACE, "Aborting scan in the middle\n" ));
break;
}
fullPath.MakePath( _pCurEntry->Filename(), _pCurEntry->FilenameSize() / sizeof(WCHAR) );
BOOL fSkipFile = FALSE;
// If it's a directory and not a reparse point, push it on the stack
if ( ( 0 != ( _pCurEntry->Attributes() & FILE_ATTRIBUTE_DIRECTORY ) ) &&
( 0 == ( _pCurEntry->Attributes() & FILE_ATTRIBUTE_REPARSE_POINT ) ) )
{
if ( _pCurEntry->Filename()[0] == L'.' &&
( _pCurEntry->FilenameSize() == sizeof(WCHAR) ||
(_pCurEntry->Filename()[1] == L'.' && _pCurEntry->FilenameSize() == sizeof(WCHAR) * 2 ) ) )
{
fSkipFile = TRUE;
}
else
{
// Neither "." nor ".." directories
Push( fullPath.GetFunnyPath() );
}
}
if ( !fSkipFile )
if ( !ProcessFile( fullPath.GetFunnyPath() ) )
{
_status = HRESULT_FROM_WIN32( GetLastError() );
return FALSE;
}
_pCurEntry = _pCurEntry->Next();
if ( 0 == _pCurEntry )
{
Status = NtQueryDirectoryFile( h, // File
0, // Event
0, // APC routine
0, // APC context
&IoStatus, // I/O Status
_xbBuf.GetPointer(), // Buffer
_xbBuf.SizeOf(), // Buffer Length
FileBothDirectoryInformation,
0, // Multiple entry
0, // Filename
0 ); // Continue scan
if ( NT_SUCCESS(Status) )
Status = IoStatus.Status;
if ( NT_SUCCESS( Status ) )
_pCurEntry = (CDirEntry *)_xbBuf.GetPointer();
else
fMore = FALSE;
}
} while ( fMore );
if ( !_fAbort )
{
if ( STATUS_NO_SUCH_FILE == Status )
Status = STATUS_NO_MORE_FILES;
_status = Status;
return STATUS_NO_MORE_FILES == _status;
}
return FALSE;
}
//+-------------------------------------------------------------------------
//
// Member: CUpdate::CUpdate, public
//
// Synopsis: Perform update of scope. All work done from constructor.
//
// Arguments: [cat] -- Catalog
// [lcaseFunnyRootPath] -- Root of scope
// [PartId] -- Partition id
// [fIncrem] -- TRUE if this is an incremental update
//
// History: 04-Jan-96 KyleP Added header
//
//--------------------------------------------------------------------------
CUpdate::CUpdate ( CiCat& cat,
ICiManager & ciManager,
const CLowerFunnyPath & lcaseFunnyRootPath,
PARTITIONID PartId,
BOOL fIncrem,
BOOL fDoDeletions,
BOOL & fAbort,
BOOL fProcessRoot )
: CTraverse( cat, fAbort, fProcessRoot ),
_ciManager(ciManager),
_cDoc(0),
_PartId(PartId),
_fIncrem(fIncrem),
_fDoDeletions(fDoDeletions)
{
ciDebugOut(( DEB_ITRACE, "CUpdate::CUpdate root: '%ws', fAbort: %d\n",
lcaseFunnyRootPath.GetActualPath(), fAbort ));
BOOL fRootDeleted = FALSE;
//
// If the rootPath is a network path, it is possible that the connection
// is down. _cat.StartUpdate() is a fairly expensive operation because it
// involves scanning the whole property store.
// Before doing any further processing, just check if we can
// open the path and get some information on it.
//
// Also, check that the root of the scope exists. If not, delete the
// files in the scope
//
if ( CImpersonateRemoteAccess::IsNetPath( lcaseFunnyRootPath.GetActualPath() ) )
{
CImpersonateRemoteAccess remote( _cat.GetImpersonationTokenCache() );
TRY
{
CImpersonatedGetAttr getAttr( lcaseFunnyRootPath );
getAttr.DoWork( remote );
}
CATCH( CException, e )
{
ciDebugOut(( DEB_WARN,
"CUpdate::CUpdate(a) can't get attrinfo: %#x, for %ws\n",
e.GetErrorCode(), lcaseFunnyRootPath.GetActualPath()));
if ( HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) != e.GetErrorCode() ||
HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) != e.GetErrorCode())
RETHROW();
fRootDeleted = TRUE;
}
END_CATCH;
}
else
{
DWORD dw = GetFileAttributes( lcaseFunnyRootPath.GetPath() );
if ( 0xffffffff == dw )
{
DWORD dwErr = GetLastError();
ciDebugOut(( DEB_WARN,
"CUpdate::CUpdate(b) can't get attrinfo: %d, for %ws\n",
dwErr, lcaseFunnyRootPath.GetPath()));
if ( ERROR_FILE_NOT_FOUND == dwErr || ERROR_PATH_NOT_FOUND == dwErr )
fRootDeleted = TRUE;
}
}
_docList.SetPartId ( _PartId );
_cat.StartUpdate( &_timeLastUpd, lcaseFunnyRootPath.GetActualPath(), fDoDeletions, eScansArray );
// Make sure EndProcessing deletes all the files in the scope
if ( fRootDeleted )
{
_status = STATUS_NO_MORE_FILES;
return;
}
//
// This is a bit of a kludge. If we're doing an incremental update, we
// will not *update* the root, but we need to mark it as seen so it
// won't get deleted.
//
if ( _fDoDeletions )
{
WORKID wid = _cat.PathToWorkId( lcaseFunnyRootPath, eScansArray, fileIdInvalid );
//
// Workid is invalid for root, such as f:\
//
//NOTE: LokSetSeen is called by PathToWorkId
//if ( wid != widInvalid )
// _cat.Touch( wid, eScansArray );
}
# if CIDBG == 1
SYSTEMTIME time;
RtlZeroMemory( &time, sizeof(time) );
FileTimeToSystemTime( &_timeLastUpd, &time );
TIME_ZONE_INFORMATION tzinfo;
GetTimeZoneInformation( &tzinfo );
SYSTEMTIME timeLocal;
SystemTimeToTzSpecificLocalTime( &tzinfo, &time, &timeLocal );
ciDebugOut(( DEB_ITRACE, "Begin update. Last update %4d/%02d/%02d %02d:%02d:%02d.%d\n",
timeLocal.wYear,
timeLocal.wMonth,
timeLocal.wDay,
timeLocal.wHour,
timeLocal.wMinute,
timeLocal.wSecond,
timeLocal.wMilliseconds ));
# endif
DoIt( lcaseFunnyRootPath );
} //CUpdate
//+-------------------------------------------------------------------------
//
// Member: CUpdate::EndProcessing, public
//
// Synopsis: Flush final updates to catalog.
//
// History: 04-Jan-96 KyleP Added header
//
//--------------------------------------------------------------------------
void CUpdate::EndProcessing()
{
ciDebugOut(( DEB_ITRACE,
"CUpdate::EndProcessing, _fAbort: %d, _status %#x\n",
_fAbort,
_status ));
if ( !_fAbort )
{
if ( _cDoc != 0 )
{
_docList.LokSetCount ( _cDoc );
_cat.AddDocuments( _docList );
_docList.LokClear();
_docList.SetPartId ( _PartId );
_cDoc = 0;
}
if ( STATUS_NO_MORE_FILES == _status )
{
_cat.EndUpdate( _fDoDeletions, eScansArray );
}
else if ( STATUS_SUCCESS != _status )
{
ciDebugOut(( DEB_ERROR, "Error %#x while traversing\n",
_status ));
THROW( CException( _status ) );
}
}
} //EndProcessing
//+---------------------------------------------------------------------------
//
// Member: CUpdate::ProcessFile
//
// Synopsis: Processes the given file and adds it to the list of changed
// documents if necessary.
//
// Arguments: [lcaseFunnyPath] - Path of the file to be processed.
//
// Returns: TRUE if successful; FALSE o/w
//
// History: 3-15-96 srikants Split from CUpdate::Traverse
//
//----------------------------------------------------------------------------
BOOL CUpdate::ProcessFile( const CLowerFunnyPath & lcaseFunnyPath )
{
if (lcaseFunnyPath.IsRemote())
{
// Need to impersonate only for the remote case
CImpersonateSystem impersonate;
return ProcessFileInternal(lcaseFunnyPath);
}
else
return ProcessFileInternal(lcaseFunnyPath);
} //ProcessFile
//+---------------------------------------------------------------------------
//
// Member: CUpdate::ProcessFileInternal
//
// Synopsis: Processes the given file and adds it to the list of changed
// documents if necessary.
//
// Arguments: [lcaseFunnyPath] - Path of the file to be processed.
//
// Returns: TRUE if successful; FALSE o/w
//
// History: 3-15-96 srikants Split from CUpdate::Traverse
//
//----------------------------------------------------------------------------
BOOL CUpdate::ProcessFileInternal( const CLowerFunnyPath & lcaseFunnyPath )
{
//
// Add path without committing it
//
FILETIME ftLastSeen = { 0,0 };
BOOL fNew;
WORKID wid = _cat.PathToWorkId( lcaseFunnyPath,
TRUE,
fNew,
_fIncrem ? &ftLastSeen : 0,
eScansArray,
fileIdInvalid );
if (wid == widInvalid)
return TRUE;
//
// Decide if file has been updated.
//
LONG result = -1;
if ( _fIncrem && !fNew && !CiCat::IsMaxTime( ftLastSeen ) )
{
//
// Use the bigger of the last seen time and time of last update
// to compare the current time of the file.
//
if ( ( ( 0 != ftLastSeen.dwLowDateTime ) ||
( 0 != ftLastSeen.dwHighDateTime ) ) &&
( CompareFileTime( &_timeLastUpd, &ftLastSeen ) > 0 ) )
{
ftLastSeen = _timeLastUpd;
}
//
// On FAT volumes, ChangeTime is always 0. On NTFS, ChangeTime
// can be more recent than ModifyTime (alias LastWriteTime) if the
// change was to the ACLs of a file, in which case we need to
// honor the change.
//
FILETIME ftLastDelta;
if ( 0 == _pCurEntry->ChangeTime() )
ftLastDelta = _pCurEntry->ModifyTimeAsFILETIME();
else
ftLastDelta = _pCurEntry->ChangeTimeAsFILETIME();
result = CompareFileTime( &ftLastSeen, &ftLastDelta );
}
if (result < 0) // The file is newer than the catalog
{
ciDebugOut(( DEB_CAT, "Update %ws\n", lcaseFunnyPath.GetActualPath() ));
_cat.WriteFileAttributes( wid, _pCurEntry->Attributes() );
Add ( wid );
}
return TRUE;
} //ProcessFileInternal
//+---------------------------------------------------------------------------
//
// Member: CUpdate::IsEligibleForTraversal
//
// Synopsis: Checks to see if the current directory is eligible for
// traversal.
//
// Arguments: [lcaseFunnyDir] -
//
// History: 3-15-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CUpdate::IsEligibleForTraversal( const CLowerFunnyPath & lcaseFunnyDir ) const
{
return _cat.IsEligibleForFiltering( lcaseFunnyDir );
}
//+-------------------------------------------------------------------------
//
// Member: CUpdate::Add, public
//
// Synopsis: Add wid for update to doclist
//
// Arguments: [wid] -- Workid
//
// History: 04-Jan-96 KyleP Added header
//
//--------------------------------------------------------------------------
void CUpdate::Add( WORKID wid )
{
_cat.Touch(wid, eScansArray);
USN usn = 0;
_docList.Set ( _cDoc, wid, usn, CI_VOLID_USN_NOT_ENABLED );
_cDoc++;
if (_cDoc == CI_MAX_DOCS_IN_WORDLIST)
{
_docList.LokSetCount ( _cDoc );
_cat.AddDocuments( _docList );
_docList.LokClear();
_docList.SetPartId ( _PartId );
_cDoc = 0;
}
}
//+---------------------------------------------------------------------------
//
// Member: CRenameDir::CRenameDir
//
// Synopsis: Constructor
//
// Arguments: [strings] -- Strings class
// [lowerFunnyDirOldName] -- Old directory
// [lowerFunnyDirNewName] -- Renamed directory
// [fAbort] -- Set to TRUE for aborting
// [volumeId] -- Volume id
//
// History: 20-Mar-96 SitaramR Created
//
//----------------------------------------------------------------------------
CRenameDir::CRenameDir( CiCat & cicat,
const CLowerFunnyPath & lowerFunnyDirOldName,
const CLowerFunnyPath & lowerFunnyDirNewName,
BOOL & fAbort,
VOLUMEID volumeId )
: CTraverse( cicat, fAbort, TRUE ),
_cicat(cicat),
_volumeId(volumeId),
_lowerFunnyDirOldName( lowerFunnyDirOldName )
{
_lowerFunnyDirOldName.AppendBackSlash();
_uLenDirOldName = _lowerFunnyDirOldName.GetActualLength();
_uLenDirNewName = lowerFunnyDirNewName.GetActualLength();
//
// Recursive traversal of renamed dir (include the renamed dir also)
//
DoIt( lowerFunnyDirNewName );
}
//+---------------------------------------------------------------------------
//
// Member: CRenameDir::ProcessFile
//
// Synopsis: Update indexes to reflect new file name
//
// Arguments: [lcaseFunnyFileNewName] - File name
//
// History: 20-Mar-96 SitaramR Created
//
//----------------------------------------------------------------------------
BOOL CRenameDir::ProcessFile( const CLowerFunnyPath & lcaseFunnyFileNewName )
{
unsigned uLenFileNewName = lcaseFunnyFileNewName.GetActualLength();
Win4Assert( uLenFileNewName >= _uLenDirNewName );
_lowerFunnyDirOldName.Truncate( _uLenDirOldName );
_lowerFunnyDirOldName.AppendBackSlash();
unsigned posNewFileNameStart = _uLenDirNewName;
if ( L'\\' == lcaseFunnyFileNewName.GetActualPath()[posNewFileNameStart] )
{
posNewFileNameStart++;
}
_lowerFunnyDirOldName.AppendPath( lcaseFunnyFileNewName.GetActualPath() + posNewFileNameStart,
uLenFileNewName - posNewFileNameStart );
_lowerFunnyDirOldName.RemoveBackSlash();
CLowerFunnyPath lcaseFunnyFileNewName2( lcaseFunnyFileNewName );
lcaseFunnyFileNewName2.RemoveBackSlash();
_cicat.RenameFile( _lowerFunnyDirOldName,
lcaseFunnyFileNewName2,
_pCurEntry->Attributes(),
_volumeId,
fileIdInvalid );
return TRUE;
}