541 lines
18 KiB
C++
541 lines
18 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 2000
|
|
//
|
|
// File: usntree.cxx
|
|
//
|
|
// Contents: Tree traversal for usn scopes
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <ntopen.hxx>
|
|
#include <pathpars.hxx>
|
|
#include <cifrmcom.hxx>
|
|
#include <funypath.hxx>
|
|
|
|
#include "cicat.hxx"
|
|
#include "usntree.hxx"
|
|
#include "usnmgr.hxx"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::CUsnTreeTraversal
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// Arguments: [cicat] -- Catalog
|
|
// [ciManager] -- CI manager
|
|
// [lcaseFunnyRootPath] -- Root of scope
|
|
// [fDoDeletions] -- Should deletions be done ?
|
|
// [fAbort] -- Abort flag
|
|
// [fProcessRoot] -- Process root ?
|
|
// [volumeId] -- Volume id
|
|
// [usnLow] -- Ignore files with USN < [usnLow]
|
|
// [usnHigh] -- Ignore files with USN > [usnHigh]
|
|
// [fUserInitiated] -- TRUE if the user asked for this
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CUsnTreeTraversal::CUsnTreeTraversal( CiCat& cicat,
|
|
CUsnMgr & usnMgr,
|
|
ICiManager & ciManager,
|
|
const CLowerFunnyPath & lcaseFunnyRootPath,
|
|
BOOL fDoDeletions,
|
|
BOOL & fAbort,
|
|
BOOL fProcessRoot,
|
|
VOLUMEID volumeId,
|
|
USN const & usnLow,
|
|
USN const & usnHigh,
|
|
BOOL fUserInitiated )
|
|
: CTraverse( cicat, fAbort, fProcessRoot ),
|
|
_usnLow( usnLow ),
|
|
_usnHigh( usnHigh ),
|
|
_cicat(cicat),
|
|
_usnMgr( usnMgr ),
|
|
_ciManager(ciManager),
|
|
_cDoc(0),
|
|
_fDoDeletions(fDoDeletions),
|
|
_volumeId(volumeId),
|
|
_cProcessed(0),
|
|
_fUserInitiated( fUserInitiated )
|
|
{
|
|
BOOL fRootDeleted = FALSE;
|
|
|
|
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 ( 1 );
|
|
FILETIME fTime;
|
|
_cicat.StartUpdate( &fTime, lcaseFunnyRootPath.GetActualPath(), fDoDeletions, eUsnsArray );
|
|
|
|
// Make sure EndProcessing deletes all the files in the scope
|
|
|
|
if ( fRootDeleted )
|
|
{
|
|
_status = STATUS_NO_MORE_FILES;
|
|
return;
|
|
}
|
|
|
|
ciDebugOut(( DEB_ITRACE, "usntree: usnlow %#I64x, usnHigh %I64x\n",
|
|
usnLow, usnHigh ));
|
|
|
|
if ( _fDoDeletions )
|
|
{
|
|
WORKID wid = _cat.PathToWorkId( lcaseFunnyRootPath, eUsnsArray, fileIdInvalid );
|
|
|
|
//
|
|
// Workid is invalid for root, such as f:\
|
|
//
|
|
if ( wid != widInvalid )
|
|
_cicat.Touch( wid, eUsnsArray );
|
|
}
|
|
|
|
DoIt( lcaseFunnyRootPath );
|
|
} //CUsnTreeTraversal
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::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
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CUsnTreeTraversal::ProcessFile( const CLowerFunnyPath & lcaseFunnyPath )
|
|
{
|
|
ciDebugOut(( DEB_USN, "Usn process file %ws\n", lcaseFunnyPath.GetActualPath() ));
|
|
|
|
USN usn;
|
|
FILEID fileId;
|
|
WORKID widParent;
|
|
FILETIME ftLastChange;
|
|
|
|
BOOL fOk = GetUsnInfo( lcaseFunnyPath,
|
|
_cicat,
|
|
_volumeId,
|
|
usn,
|
|
fileId,
|
|
widParent,
|
|
ftLastChange );
|
|
|
|
if ( fOk )
|
|
{
|
|
WORKID wid;
|
|
|
|
BOOL fAdded = _cicat.ProcessFile( lcaseFunnyPath,
|
|
fileId,
|
|
_volumeId,
|
|
widParent,
|
|
_pCurEntry->Attributes(),
|
|
wid );
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"wid %d(%x), fAdded %d, _fUserInitiated %d, usn %#I64x,"
|
|
" _usnLow %#I64x, _usnHigh %#I64x\n",
|
|
wid, wid, fAdded, _fUserInitiated, usn,
|
|
_usnLow, _usnHigh ));
|
|
|
|
if ( widInvalid != wid )
|
|
{
|
|
#if 0 // NOTE: we can't use this optimization because we don't know why the
|
|
// USN is higher than _usnHigh. We might later choose to not
|
|
// filter based on this USN change.
|
|
if ( usn > _usnHigh )
|
|
{
|
|
// Modified since scan began. Don't file twice.
|
|
_cicat.Touch( wid, eUsnsArray );
|
|
}
|
|
else
|
|
#endif // 0
|
|
if ( _fUserInitiated || ( 0 == usn ) )
|
|
{
|
|
// User asked to filter everything or NT4 create
|
|
Add ( wid );
|
|
}
|
|
else if ( 0 == _usnLow )
|
|
{
|
|
// Initial scan -- filter if we added the file OR
|
|
// if the file has changed since we filtered it last.
|
|
|
|
if ( fAdded || _cicat.HasChanged( wid, ftLastChange ) )
|
|
{
|
|
Add ( wid );
|
|
}
|
|
else
|
|
{
|
|
// Make sure we don't delete it!
|
|
_cicat.Touch(wid, eUsnsArray);
|
|
}
|
|
}
|
|
else if ( usn >= _usnLow )
|
|
{
|
|
Add ( wid );
|
|
}
|
|
else if ( _cicat.HasChanged( wid, ftLastChange ) )
|
|
{
|
|
// NT4 Modify
|
|
Add( wid );
|
|
}
|
|
else
|
|
{
|
|
// Make sure we don't delete it!
|
|
_cicat.Touch(wid, eUsnsArray);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
} //ProcessFile
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::TraversalIdle, public
|
|
//
|
|
// Synopsis: Called when a directory is about to be traversed, when
|
|
// the traversal code isn't buffering any paths. This makes
|
|
// it a good time to check the USN log, since the Win32 file
|
|
// enumeration code buffers files within a given directory.
|
|
//
|
|
// Arguments: [fStalled] -- TRUE implies scanning halted due to resource
|
|
// constraints. Only critical work should be done.
|
|
//
|
|
// History: 16-Jun-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CUsnTreeTraversal::TraversalIdle( BOOL fStalled )
|
|
{
|
|
//
|
|
// Every now and then, go empty the USN logs so they won't overflow
|
|
// during the scan.
|
|
//
|
|
|
|
if ( _cProcessed > 1000 || fStalled )
|
|
{
|
|
ciDebugOut(( DEB_USN, "CUsnTreeTraversal: ProcessUsnLog\n" ));
|
|
_usnMgr.ProcessUsnLog( _fAbort, _volumeId, _usnHigh );
|
|
|
|
if ( _cProcessed > 1000 )
|
|
_cProcessed = 0;
|
|
}
|
|
} //ProcessDirectoryTraversal
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::Add
|
|
//
|
|
// Synopsis: Add wid for update to doclist
|
|
//
|
|
// Arguments: [wid] -- Workid
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CUsnTreeTraversal::Add( WORKID wid )
|
|
{
|
|
_cicat.Touch(wid, eUsnsArray);
|
|
|
|
ciDebugOut(( DEB_USN, " Add %d(%x) as doc %d\n", wid, wid, _cDoc ));
|
|
|
|
//
|
|
// Use an usn of 0, because we are adding wids in tree order, not usn order
|
|
//
|
|
_docList.Set ( _cDoc, wid, 0, _volumeId );
|
|
|
|
_cDoc++;
|
|
|
|
if (_cDoc == CI_MAX_DOCS_IN_WORDLIST)
|
|
{
|
|
_cProcessed += _cDoc;
|
|
|
|
_docList.LokSetCount ( _cDoc );
|
|
|
|
_cicat.AddDocuments( _docList );
|
|
|
|
_docList.LokClear();
|
|
_docList.SetPartId ( 1 );
|
|
_cDoc = 0;
|
|
|
|
// NTRAID#DB-NTBUG9-83800-2000/07/31-dlee notifications can be missed if > 10k files in a directory
|
|
//
|
|
// This will hit if a directory has > 10k files. In this case, we
|
|
// need to read the USN log so it won't overflow, but we may
|
|
// mis-interpret the scan since the Win32 enumeration API buffers
|
|
// a small number of files during the enumeration. This is a lesser
|
|
// of 2 evils fix. A file will be falsely added to the catalog if it
|
|
// is in the enumeration buffer buffer, then deleted before it
|
|
// is processed. This window has existed on FAT for a long time
|
|
// and hasn't been hit.
|
|
//
|
|
|
|
if ( _cProcessed > 10000 )
|
|
{
|
|
ciDebugOut(( DEB_USN, "Add: ProcessUsnLog\n" ));
|
|
_usnMgr.ProcessUsnLog( _fAbort, _volumeId, _usnHigh );
|
|
_cProcessed = 0;
|
|
}
|
|
}
|
|
} //Add
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::IsEligibleForTraversal
|
|
//
|
|
// Synopsis: Checks to see if the current directory is eligible for
|
|
// traversal.
|
|
//
|
|
// Arguments: [lcaseFunnyDir] - Directory path
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CUsnTreeTraversal::IsEligibleForTraversal( const CLowerFunnyPath & lcaseFunnyDir ) const
|
|
{
|
|
return _cicat.IsEligibleForFiltering( lcaseFunnyDir );
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::EndProcessing
|
|
//
|
|
// Synopsis: Flush final updates to catalog
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CUsnTreeTraversal::EndProcessing()
|
|
{
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"CUsnTreeTraversal__EndProcessing, _fAbort %d, _cDoc %d, _status %#x\n",
|
|
_fAbort, _cDoc, _status ));
|
|
|
|
if ( !_fAbort )
|
|
{
|
|
if ( _cDoc != 0 )
|
|
{
|
|
_docList.LokSetCount ( _cDoc );
|
|
_cicat.AddDocuments( _docList );
|
|
_docList.LokClear();
|
|
_docList.SetPartId ( 1 );
|
|
_cDoc = 0;
|
|
}
|
|
|
|
if ( STATUS_NO_MORE_FILES == _status )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "_fDoDeletions %d\n", _fDoDeletions ));
|
|
_cat.EndUpdate( _fDoDeletions, eUsnsArray );
|
|
}
|
|
else if ( STATUS_SUCCESS != _status )
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %#x while traversing\n",
|
|
_status ));
|
|
THROW( CException( _status ) );
|
|
}
|
|
}
|
|
} //EndProcessing
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CUsnTreeTraversal::GetUsnInfo
|
|
//
|
|
// Synopsis: Returns fileid etc info for given file
|
|
//
|
|
// Arguments: [funnyPath] -- Path to file
|
|
// [cicat] -- Catalog
|
|
// [volumeId] -- Volume id
|
|
// [usn] -- Usn returned here
|
|
// [fileId] -- FileId returned here
|
|
// [widParent] -- Workid of parent returned here
|
|
// [ftLastChange] -- Time of last change (data, EA, security, ...)
|
|
//
|
|
// History: 07-May-97 SitaramR Created
|
|
//
|
|
// Notes: The routine doesn't throw, it returns false to indicate that
|
|
// the tree traversal must continue after skipping over the
|
|
// current file.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL CUsnTreeTraversal::GetUsnInfo( const CFunnyPath & funnyPath,
|
|
CiCat &cicat,
|
|
VOLUMEID volumeId,
|
|
USN &usn,
|
|
FILEID &fileId,
|
|
WORKID &widParent,
|
|
FILETIME &ftLastChange )
|
|
{
|
|
HANDLE hFile;
|
|
NTSTATUS status = CiNtOpenNoThrow( hFile,
|
|
funnyPath.GetPath(),
|
|
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
0 );
|
|
|
|
if ( !NT_SUCCESS( status ) )
|
|
{
|
|
// File may have been deleted, in use etc, so skip processing the file.
|
|
|
|
ciDebugOut(( DEB_ITRACE,
|
|
"File open for %ws get usn info failed (0x%x)\n",
|
|
funnyPath.GetPath(), status ));
|
|
|
|
#if CIDBG == 1
|
|
if ( IsSharingViolation( status ) ||
|
|
STATUS_ACCESS_DENIED == status ||
|
|
STATUS_INVALID_PARAMETER == status ||
|
|
STATUS_DELETE_PENDING == status ||
|
|
STATUS_UNRECOGNIZED_VOLUME == status ||
|
|
STATUS_INSUFFICIENT_RESOURCES == status ||
|
|
STATUS_OBJECT_PATH_NOT_FOUND == status ||
|
|
STATUS_OBJECT_NAME_NOT_FOUND == status ||
|
|
STATUS_IO_REPARSE_TAG_NOT_HANDLED == status ||
|
|
STATUS_NO_MEDIA_IN_DEVICE == status || // junction points can hit this
|
|
STATUS_OBJECT_NAME_INVALID == status )
|
|
return FALSE;
|
|
else
|
|
{
|
|
#if CIDBG == 1
|
|
char szTemp[200];
|
|
sprintf( szTemp, "New error 0x%x from NtCreateFile in ::GetUsnInfo. Call dlee or hit 'g'", status );
|
|
Win4AssertEx(__FILE__, __LINE__, szTemp);
|
|
#endif
|
|
|
|
// THROW( CException( status ) );
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SHandle xFile( hFile );
|
|
|
|
|
|
// NTRAID#DB-NTBUG9-83802-2000/07/31-dlee File names are limited to 400 characters when reading USN records
|
|
|
|
IO_STATUS_BLOCK iosb;
|
|
ULONGLONG readBuffer[100];
|
|
USN_RECORD *pUsnRecord;
|
|
status = NtFsControlFile( hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&iosb,
|
|
FSCTL_READ_FILE_USN_DATA,
|
|
NULL,
|
|
NULL,
|
|
&readBuffer,
|
|
sizeof(readBuffer) );
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
status = iosb.Status;
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
{
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
status = NtQueryInformationFile( hFile,
|
|
&iosb,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation );
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
status = iosb.Status;
|
|
|
|
if ( NT_SUCCESS( status ) )
|
|
{
|
|
pUsnRecord = (USN_RECORD *) &readBuffer;
|
|
fileId = pUsnRecord->FileReferenceNumber;
|
|
|
|
//
|
|
// If not indexed, return FALSE unless it's a directory and it's
|
|
// already in the index. We need to leave these in case there
|
|
// are files below the directory.
|
|
//
|
|
|
|
if ( ( 0 != (pUsnRecord->FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) ) )
|
|
{
|
|
if ( 0 == (pUsnRecord->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
|
|
return FALSE;
|
|
|
|
WORKID wid = cicat.FileIdToWorkId( fileId, volumeId );
|
|
|
|
if ( widInvalid == wid )
|
|
return FALSE;
|
|
|
|
ciDebugOut(( DEB_ITRACE, "leaving special-case directory\n" ));
|
|
}
|
|
|
|
usn = pUsnRecord->Usn;
|
|
|
|
//
|
|
// Workid of parent will be widInvalid for files in root directory
|
|
//
|
|
widParent = cicat.FileIdToWorkId( pUsnRecord->ParentFileReferenceNumber,
|
|
volumeId );
|
|
|
|
ftLastChange = *(FILETIME *)&BasicInfo.ChangeTime;
|
|
}
|
|
else
|
|
{
|
|
// NTRAID#DB-NTBUG9-83804-2000/07/31-dlee When a lookup of USN info fails during a USN scan due to low resources we don't abort the scan
|
|
//
|
|
// Incorrect behavior if out of resources -- we need to
|
|
// restart the scan!
|
|
//
|
|
// Need to handle reparse point errors like
|
|
// STATUS_IO_REPARSE_TAG_NOT_HANDLED. For now, ignore
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE, "NtQueryInformationFile failed, 0x%x\n", status ));
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// NTRAID#DB-NTBUG9-83804-2000/07/31-dlee When a lookup of USN info fails during a USN scan due to low resources we don't abort the scan
|
|
|
|
// incorrect behavior if out of resources -- we need to
|
|
// restart the scan!
|
|
//
|
|
// Need to handle reparse point errors like
|
|
// STATUS_IO_REPARSE_TAG_NOT_HANDLED. For now, ignore
|
|
//
|
|
|
|
ciDebugOut(( DEB_ITRACE, "File usn read fsctl failed, 0x%x\n", status ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
} //GetUsnInfo
|
|
|