windows-nt/Source/XPSP1/NT/sdktools/fsdump/lib/exclproc.cpp

773 lines
22 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}