|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
exclproc.cpp
Abstract:
Exclude processing mechanism. Processes the FilesNotToBackup key and zero or more exclude files with exclude rules.
Author:
Stefan R. Steiner [ssteiner] 03-21-2000
Revision History:
--*/
#include "stdafx.h"
#include "match.h"
#include <shlobj.h>
static VOID FsdExpandEnvironmentStrings( IN LPCWSTR pwszInput, OUT CBsString &cwsExpandedStr );
static BOOL FsdEnsureLongNames( IN OUT CBsString& exclude_spec );
SFsdExcludeRule::~SFsdExcludeRule() { delete( psVolId ); psVolId = NULL; }
/*++
Routine Description:
Prints out information about one rule. If the rule caused files to be excluded it prints out those file too.
Arguments:
Return Value:
<Enter return values here>
--*/ VOID SFsdExcludeRule::PrintRule( IN FILE *fpOut, IN BOOL bInvalidRulePrint ) { if ( bInvalidRulePrint ) { if ( bInvalidRule ) fwprintf( fpOut, L"%-24s %-32s '%s'\n", cwsExcludeFromSource.c_str(), cwsExcludeDescription.c_str(), cwsExcludeRule.c_str() ); } else { //
// Iterate though excluded file list
//
CBsString cwsExcludedFile; CVssDLListIterator< CBsString > cExcludedFilesIter( cExcludedFileList ); if ( cExcludedFilesIter.GetNext( cwsExcludedFile ) ) { //
// At least one file excluded, print the header for the rule
//
fwprintf( fpOut, L"%-24s %-32s '%s'\n", cwsExcludeFromSource.c_str(), cwsExcludeDescription.c_str(), cwsExcludeRule.c_str() );
//
// Now iterate
//
do { fwprintf( fpOut, L"\t%s\n", cwsExcludedFile.c_str() ); } while( cExcludedFilesIter.GetNext( cwsExcludedFile ) ); } } } CFsdExclusionManager::CFsdExclusionManager( IN CDumpParameters *pcDumpParameters ) : m_pcParams( pcDumpParameters ) { if ( !m_pcParams->m_bDontUseRegistryExcludes ) { ProcessRegistryExcludes( HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE" ); ProcessRegistryExcludes( HKEY_CURRENT_USER, L"HKEY_CURRENT_USER" ); } CBsString cwsEXEFileStreamExcludeFile( m_pcParams->m_cwsArgv0 + L":ExcludeList" ); if ( ProcessOneExcludeFile( cwsEXEFileStreamExcludeFile ) == FALSE ) m_pcParams->DumpPrint( L" NOTE: Exclude file: '%s' not found", cwsEXEFileStreamExcludeFile.c_str() ); ProcessExcludeFiles( m_pcParams->m_cwsFullPathToEXE ); CompileExclusionRules(); }
CFsdExclusionManager::~CFsdExclusionManager() { SFsdExcludeRule *pER;
//
// Iterate through the exclude rule list and delete each element
//
CVssDLListIterator< SFsdExcludeRule * > cExclRuleIter( m_cCompleteExcludeList ); while( cExclRuleIter.GetNext( pER ) ) delete pER; }
VOID CFsdExclusionManager::ProcessRegistryExcludes( IN HKEY hKey, IN LPCWSTR pwszFromSource ) { LPWSTR buffer ; HKEY key = NULL ; DWORD stat ; DWORD dwDisposition ; DWORD dwDataSize; DWORD dwIndex = 0; HRESULT hr = S_OK; m_pcParams->DumpPrint( L" Processing FilesNotToBackup reg key in %s", pwszFromSource );
buffer = new WCHAR[ FSD_MAX_PATH ]; if ( buffer == NULL ) throw E_OUTOFMEMORY;
try { stat = ::RegOpenKeyEx( hKey, FSD_REG_EXCLUDE_PATH, 0, KEY_READ, &key ) ;
dwIndex = 0 ; while ( stat == ERROR_SUCCESS ) { WCHAR pwszValue[ MAX_PATH ]; DWORD dwValSize = MAX_PATH; // prefix #118830
DWORD dwType;
dwDataSize = FSD_MAX_PATH; // prefix #118830
stat = ::RegEnumValueW( key, dwIndex, pwszValue, &dwValSize, NULL, &dwType, (LPBYTE)buffer, &dwDataSize ) ; dwIndex++;
if ( ( stat == ERROR_SUCCESS ) && ( dwType == REG_MULTI_SZ ) ) { LPWSTR p = buffer; while ( *p ) { SFsdExcludeRule *psExclRule;
//
// Now load up the exclude rule with the unprocessed
// information
//
psExclRule = new SFsdExcludeRule; if ( psExclRule == NULL ) throw E_OUTOFMEMORY; psExclRule->cwsExcludeFromSource = pwszFromSource; psExclRule->cwsExcludeDescription = pwszValue; psExclRule->cwsExcludeRule = p; if ( m_pcParams->m_bPrintDebugInfo || m_pcParams->m_bNoHeaderFooter ) { m_pcParams->DumpPrint( L" \"%s\" \"%s\"", pwszValue, p ); } m_cCompleteExcludeList.AddTail( psExclRule ); p += ::wcslen( p ) + 1; } }
} } catch ( HRESULT hrCaught ) { hr = hrCaught; } catch ( ... ) { hr = E_UNEXPECTED; } if ( key != NULL ) { ::RegCloseKey( key ) ; key = NULL ; }
delete [] buffer ;
if ( FAILED( hr ) ) throw hr; }
VOID CFsdExclusionManager::ProcessExcludeFiles( IN const CBsString& cwsPathToExcludeFiles ) { HANDLE hFind = INVALID_HANDLE_VALUE;
try { //
// Iterate through all files in the directory looking for
// files with .exclude extensions
//
DWORD dwRet; WIN32_FIND_DATAW sFindData; hFind = ::FindFirstFileExW( cwsPathToExcludeFiles + L"*.exclude", FindExInfoStandard, &sFindData, FindExSearchNameMatch, NULL, 0 ); if ( hFind == INVALID_HANDLE_VALUE ) { dwRet = ::GetLastError(); if ( dwRet == ERROR_NO_MORE_FILES || dwRet == ERROR_FILE_NOT_FOUND ) return; else { m_pcParams->ErrPrint( L"CFsdExclusionManager::ProcessExcludeFiles - FindFirstFileEx( '%s' ) returned: dwRet: %d, skipping looking for .exclude files", cwsPathToExcludeFiles.c_str(), ::GetLastError() ); return; } }
//
// Now run through the directory
//
do { // Check and make sure the file such as ".", ".." and dirs are not considered
if( ::wcscmp( sFindData.cFileName, L".") != 0 && ::wcscmp( sFindData.cFileName, L"..") != 0 && !( sFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) { ProcessOneExcludeFile( cwsPathToExcludeFiles + sFindData.cFileName ); } } while ( ::FindNextFile( hFind, &sFindData ) ); } catch ( ... ) { m_pcParams->ErrPrint( L"CFsdExclusionManager::ProcessExcludeFiles: Caught an unknown exception, dirPath: '%s'", cwsPathToExcludeFiles.c_str() ); }
if ( hFind != INVALID_HANDLE_VALUE ) ::FindClose( hFind ); }
BOOL CFsdExclusionManager::ProcessOneExcludeFile( IN const CBsString& cwsExcludeFileName ) { FILE *fpExclFile; fpExclFile = ::_wfopen( cwsExcludeFileName, L"r" ); if ( fpExclFile == NULL ) { return FALSE; } m_pcParams->DumpPrint( L" Processing exclude file: '%s'", cwsExcludeFileName.c_str() );
CBsString cwsInputLine; while( ::fgetws( cwsInputLine.GetBuffer( FSD_MAX_PATH), FSD_MAX_PATH, fpExclFile ) ) { cwsInputLine.ReleaseBuffer(); cwsInputLine = cwsInputLine.Left( cwsInputLine.GetLength() - 1 ); // get rid of '\n'
cwsInputLine.TrimLeft(); cwsInputLine.TrimRight();
//
// See if it is a comment, either // or #
//
if ( cwsInputLine[ 0 ] == L'#' || cwsInputLine.Left( 2 ) == L"//" || cwsInputLine.IsEmpty() ) continue; if ( m_pcParams->m_bPrintDebugInfo || m_pcParams->m_bNoHeaderFooter ) { m_pcParams->DumpPrint( L" %s", cwsInputLine.c_str() ); }
CBsString cwsLine( cwsInputLine ); SFsdExcludeRule *psExclRule; psExclRule = new SFsdExcludeRule; if ( psExclRule == NULL ) { ::fclose( fpExclFile ); throw E_OUTOFMEMORY; } INT iLeft; INT iRight; //
// This is gross. With the updated string class, this can
// be simplified.
//
iLeft = cwsLine.Find( L'\"' ); if ( iLeft != -1 ) { cwsLine = cwsLine.Mid( iLeft + 1 ); iRight = cwsLine.Find( L'\"' ); if ( iRight != -1 ) { psExclRule->cwsExcludeDescription = cwsLine.Left( iRight ); cwsLine = cwsLine.Mid( iRight + 1 ); iLeft = cwsLine.Find( L'\"' ); if ( iLeft != -1 ) { cwsLine = cwsLine.Mid( iLeft + 1 ); iRight = cwsLine.Find( L'\"' ); if ( iRight != -1 ) { psExclRule->cwsExcludeRule = cwsLine.Left( iRight ); psExclRule->cwsExcludeFromSource = cwsExcludeFileName.c_str() + cwsExcludeFileName.ReverseFind( L'\\' ) + 1; m_cCompleteExcludeList.AddTail( psExclRule ); continue; } } } } else { m_pcParams->ErrPrint( L"Parse error in exclusion rule file '%s', rule text '%s', skipping", cwsExcludeFileName.c_str(), cwsInputLine.c_str() ); } delete psExclRule; }
::fclose( fpExclFile );
return TRUE; }
VOID CFsdExclusionManager::CompileExclusionRules() { SFsdExcludeRule *psER;
//
// Iterate through the exclude rule list and compile each rule
//
CVssDLListIterator< SFsdExcludeRule * > cExclRuleIter( m_cCompleteExcludeList ); CBsString cws; if ( m_pcParams->m_bPrintDebugInfo ) wprintf( L"Exclusion rule debug info:\n" ); while( cExclRuleIter.GetNext( psER ) ) { INT i; ::FsdExpandEnvironmentStrings( psER->cwsExcludeRule, cws );
if ( m_pcParams->m_bPrintDebugInfo ) { wprintf( L"\t%s : %s : %s : %s", psER->cwsExcludeFromSource.c_str(), psER->cwsExcludeDescription.c_str(), psER->cwsExcludeRule.c_str(), cws.c_str() ); } //
// Get rid of leading spaces and lower case the whole mess
//
cws.TrimLeft(); cws.MakeUpper(); //
// First see if /s is at end of string
//
i = cws.Find( L"/S" ); if ( i > 0 ) { cws = cws.Left( i ); psER->bInclSubDirs = TRUE; } cws.TrimRight();
//
// Now see if there are any wildcards
//
i = cws.FindOneOf( L"*?" ); if ( i != -1 ) { psER->bWCInFileName = TRUE; }
//
// Now see if this is for any volume
//
if ( cws.GetLength() >= 2 && cws[0] == L'\\' && cws[1] != L'\\' ) psER->bAnyVol = TRUE; else if ( cws.GetLength() >= 2 && cws[1] != L':' ) psER->bAnyVol = TRUE;
if ( psER->bAnyVol ) { if ( cws[0] == L'\\' ) { // Get rid of first '\'
cws = cws.Mid( 1 ); } } else { //
// Specific volume case
//
CBsString cwsVolPath; psER->psVolId = new SFsdVolumeId; if ( psER->psVolId == NULL ) // Prefix 118832
{ m_pcParams->ErrPrint( L"CFsdExclusionManager::CompileExclusionRules - out of memory" ); throw E_OUTOFMEMORY; // Prefix #118832
} if ( CFsdVolumeStateManager::GetVolumeIdAndPath( m_pcParams, cws, psER->psVolId, cwsVolPath ) != ERROR_SUCCESS ) psER->bInvalidRule = TRUE; else { //
// Slice off the volume part of the path
//
cws = cws.Mid( cwsVolPath.GetLength() ); } } INT iFileNameOffset; iFileNameOffset = cws.ReverseFind( L'\\' ); if ( iFileNameOffset == -1 ) { //
// No dirpath
//
// psER->cwsDirPath = L"\\";
psER->cwsFileNamePattern = cws; } else { psER->cwsFileNamePattern = cws.Mid( iFileNameOffset + 1 ); psER->cwsDirPath = cws.Left( iFileNameOffset + 1 ); }
//
// Now convert the file name pattern into a form that the pattern matcher
// can use.
//
::FsdRtlConvertWildCards( psER->cwsFileNamePattern ); if ( m_pcParams->m_bPrintDebugInfo ) { if ( psER->bInclSubDirs ) wprintf( L" - SubDir" ); if ( psER->bWCInFileName ) wprintf( L" - WC" ); if ( psER->bAnyVol ) wprintf( L" - AnyVol" ); wprintf( L" - VolId: 0x%08x, DirPath: '%s', FileName: '%s'", ( psER->psVolId ) ? psER->psVolId->m_dwVolSerialNumber : 0xFFFFFFFF, psER->cwsDirPath.c_str(), psER->cwsFileNamePattern.c_str() );
if ( psER->bInvalidRule ) wprintf( L" - ERROR, invalid rule" ); wprintf( L"\n" ); } }
}
VOID CFsdExclusionManager::GetFileSystemExcludeProcessor( IN CBsString cwsVolumePath, IN SFsdVolumeId *psVolId, OUT CFsdFileSystemExcludeProcessor **ppcFSExcludeProcessor ) { CFsdFileSystemExcludeProcessor *pExclProc; *ppcFSExcludeProcessor = NULL; //
// Get a new exclude processor for the file system
//
pExclProc = new CFsdFileSystemExcludeProcessor( m_pcParams, cwsVolumePath, psVolId ); if ( pExclProc == NULL ) { m_pcParams->ErrPrint( L"CFsdExclusionManager::CFsdGetFileSystemExcludeProcessor - Could not new a CFsdFileSystemExcludeProcessor object" ); throw E_OUTOFMEMORY; } SFsdExcludeRule *pER;
//
// Now go through the complete exclude list to find exclude rules that are relevant to
// this file system
//
CVssDLListIterator< SFsdExcludeRule * > cExclRuleIter( m_cCompleteExcludeList ); while( cExclRuleIter.GetNext( pER ) ) { if ( !pER->bInvalidRule ) { if ( pER->bAnyVol || pER->psVolId->IsEqual( psVolId ) ) { pExclProc->m_cFSExcludeList.AddTail( pER ); } } }
*ppcFSExcludeProcessor = pExclProc; }
/*++
Routine Description:
Goes through the list of exclusion rules and dumps information about each.
Arguments:
Return Value:
<Enter return values here>
--*/ VOID CFsdExclusionManager::PrintExclusionInformation() { SFsdExcludeRule *pER;
CVssDLListIterator< SFsdExcludeRule * > cExclRuleIter( m_cCompleteExcludeList );
m_pcParams->DumpPrint( L"" ); m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"Invalid exclusion rules (invalid because volume not found or parsing error)" ); m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"From Application Exclusion rule" ); while( cExclRuleIter.GetNext( pER ) ) pER->PrintRule( m_pcParams->GetDumpFile(), TRUE ); m_pcParams->DumpPrint( L"" ); m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"Files excluded by valid exclusion rule" ); m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"From Application Exclusion rule" ); cExclRuleIter.Reset(); while( cExclRuleIter.GetNext( pER ) ) pER->PrintRule( m_pcParams->GetDumpFile(), FALSE ); }
CFsdFileSystemExcludeProcessor::CFsdFileSystemExcludeProcessor( IN CDumpParameters *pcDumpParameters, IN const CBsString& cwsVolumePath, IN SFsdVolumeId *psVolId ) : m_pcParams( pcDumpParameters ), m_cwsVolumePath( cwsVolumePath), m_psVolId( NULL ) { m_psVolId = new SFsdVolumeId; if ( m_psVolId == NULL ) // Prefix #118829
throw E_OUTOFMEMORY; *m_psVolId = *psVolId; }
CFsdFileSystemExcludeProcessor::~CFsdFileSystemExcludeProcessor() { delete m_psVolId; }
BOOL CFsdFileSystemExcludeProcessor::IsExcludedFile( IN const CBsString &cwsFullDirPath, IN DWORD dwEndOfVolMountPointOffset, IN const CBsString &cwsFileName ) { BOOL bFoundMatch = FALSE; SFsdExcludeRule *pER; CBsString cwsUpperFileName( cwsFileName ); CBsString cwsDirPath( cwsFullDirPath.Mid( dwEndOfVolMountPointOffset ) ); cwsUpperFileName.MakeUpper(); // Make uppercased for match check
cwsDirPath.MakeUpper(); // wprintf( L"Exclude proc: DirPath: %s, fileName: %s\n", cwsDirPath.c_str(), cwsUpperFileName.c_str() );
CVssDLListIterator< SFsdExcludeRule * > cExclRuleIter( m_cFSExcludeList ); while( !bFoundMatch && cExclRuleIter.GetNext( pER ) ) { if ( pER->bInclSubDirs ) { //
// First check most common case \XXX /s
//
if ( pER->cwsDirPath.GetLength() != 0 ) { if ( ::wcsncmp( pER->cwsDirPath.c_str(), cwsDirPath.c_str(), pER->cwsDirPath.GetLength() ) != 0 ) continue; } } else { //
// Fixed path check
//
if ( pER->cwsDirPath != cwsDirPath ) { continue; } } if ( pER->bWCInFileName ) { //
// Pattern match check
//
if ( ::FsdRtlIsNameInExpression( pER->cwsFileNamePattern, cwsUpperFileName ) ) bFoundMatch = TRUE; } else { //
// Constant string match
//
if ( pER->cwsFileNamePattern == cwsUpperFileName ) bFoundMatch = TRUE; } }
if ( bFoundMatch ) { pER->cExcludedFileList.AddTail( cwsFullDirPath + cwsFileName ); if ( m_pcParams->m_bPrintDebugInfo ) wprintf( L" EXCLUDING: %s%s\n", cwsFullDirPath.c_str(), cwsFileName.c_str() ); } return bFoundMatch; }
static VOID FsdExpandEnvironmentStrings( IN LPCWSTR pwszInput, OUT CBsString &cwsExpandedStr ) { BOOL isOK = FALSE; LPWSTR pwszBuffer; DWORD dwSize = ::ExpandEnvironmentStringsW( pwszInput, NULL, 0 ) ;
if ( pwszBuffer = cwsExpandedStr.GetBufferSetLength( dwSize + 1 ) ) { isOK = ( 0 != ::ExpandEnvironmentStringsW( pwszInput, pwszBuffer, dwSize ) ) ; cwsExpandedStr.ReleaseBuffer( ) ; }
if ( !isOK ) { // have never seen ExpandEnvironmentStrings fail... even with undefined env var... but just in case
cwsExpandedStr = pwszInput ; }
::FsdEnsureLongNames( cwsExpandedStr ); }
/*++
Routine Description:
Originally from NtBackup. This takes a path with short name components and expands them to long name components. The path obviously has to exist on the system to expand short names. Uses a little shell magic (yuck) to translate it into a long name. Arguments:
Return Value:
TRUE if expanded properly
--*/ static BOOL FsdEnsureLongNames( IN OUT CBsString& exclude_spec ) { IShellFolder * desktop ; ITEMIDLIST * id_list ; ULONG parsed_ct = 0 ; BOOL isOK = FALSE ; // initialize it, prefix bug # 180281
CBsString path ; int last_slash ;
// strip off the filename, and all that other detritus...
path = exclude_spec ; if ( -1 != ( last_slash = path.ReverseFind( TEXT( '\\' ) ) ) ) { path = path.Left( last_slash ) ;
if ( SUCCEEDED( SHGetDesktopFolder( &desktop ) ) ) { WCHAR * ptr = path.GetBufferSetLength( FSD_MAX_PATH ) ;
if ( SUCCEEDED( desktop->ParseDisplayName( NULL, NULL, ptr, &parsed_ct, &id_list, NULL ) ) ) { IMalloc * imalloc ;
isOK = SHGetPathFromIDList( id_list, ptr ) ;
SHGetMalloc( &imalloc ) ; imalloc->Free( id_list ) ; imalloc->Release( ) ; }
path.ReleaseBuffer( ) ; desktop->Release( ) ; }
if ( isOK ) { // put it back together with the new & improved path...
exclude_spec = path + exclude_spec.Mid( last_slash ) ; } }
return isOK; }
|