Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1545 lines
60 KiB

/*
**++
**
** Copyright (c) 2002 Microsoft Corporation
**
**
** Module Name:
**
** swriter.cpp
**
**
** Abstract:
**
** Test program to to register a Writer with various properties
**
** Author:
**
** Reuven Lax [reuvenl] 04-June-2002
**
**
** Revision History:
**
**--
*/
///////////////////////////////////////////////////////////////////////////////
// Includes
#include "stdafx.h"
#include "swriter.h"
#include "utility.h"
#include "writerconfig.h"
#include <string>
#include <sstream>
#include <functional>
#include <algorithm>
#include <queue>
///////////////////////////////////////////////////////////////////////////////
// Declarations and Definitions
using std::wstring;
using std::string;
using std::wstringstream;
using std::exception;
using std::vector;
using Utility::checkReturn;
using Utility::warnReturn;
using Utility::printStatus;
static const wchar_t* const BackupString = L"BACKUP";
static const wchar_t* const RestoreString = L"RESTORE";
///////////////////////////////////////////////////////////////////////////////
// Initialize the test writer
HRESULT STDMETHODCALLTYPE TestWriter::Initialize()
{
WriterConfiguration* config = WriterConfiguration::instance();
printStatus(L"Initializing Writer", Utility::high);
HRESULT hr = CVssWriter::Initialize(TestWriterId, // WriterID
TestWriterName, // wszWriterName
config->usage(), // ut
VSS_ST_OTHER); // st
checkReturn(hr, L"CVssWriter::Initialize");
hr = Subscribe();
checkReturn(hr, L"CVssWriter::Subscribe");
return S_OK;
}
// OnIdentify is called as a result of the requestor calling GatherWriterMetadata
// Here we report the writer metadata using the passed-in interface
bool STDMETHODCALLTYPE TestWriter::OnIdentify(IN IVssCreateWriterMetadata *pMetadata)
try
{
enterEvent(Utility::Identify);
WriterConfiguration* config = WriterConfiguration::instance();
// set the restore method properly
RestoreMethod method = config->restoreMethod();
HRESULT hr = pMetadata->SetRestoreMethod(method.m_method,
method.m_service.c_str(),
NULL,
method.m_writerRestore,
method.m_rebootRequired);
checkReturn(hr, L"IVssCreateWriterMetadata::SetRestoreMethod");
printStatus(L"\nSet restore method: ", Utility::high);
printStatus(method.toString(), Utility::high);
// set the alternate-location list
RestoreMethod::AlternateList::iterator currentAlt = method.m_alternateLocations.begin();
while (currentAlt != method.m_alternateLocations.end()) {
hr = pMetadata->AddAlternateLocationMapping(currentAlt->m_path. c_str(),
currentAlt->m_filespec.c_str(),
currentAlt->m_recursive,
currentAlt->m_alternatePath.substr(0, currentAlt->m_alternatePath.size()-1).c_str());
checkReturn(hr, L"IVssCreateWriterMetadata::AddAlternateLocationMapping");
printStatus(L"\nAdded Alternate Location Mapping");
printStatus(currentAlt->toString());
++currentAlt;
}
// set the exclude-file list
WriterConfiguration::ExcludeFileList::iterator currentExclude = config->excludeFiles().begin();
while (currentExclude != config->excludeFiles().end()) {
hr = pMetadata->AddExcludeFiles(currentExclude->m_path.c_str(),
currentExclude->m_filespec.c_str(),
currentExclude->m_recursive);
checkReturn(hr, L"IVssCreateWriterMetadata::AddExcludeFiles");
printStatus(L"\nAdded exclude filespec");
printStatus(currentExclude->toString());
++currentExclude;
}
// add all necessary components
WriterConfiguration::ComponentList::iterator currentComponent = config->components().begin();
while (currentComponent != config->components().end()) {
addComponent(*currentComponent, pMetadata);
++currentComponent;
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in Identify event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
};
// This function is called as a result of the requestor calling PrepareForBackup
// Here we do some checking to ensure that the requestor selected components properly
bool STDMETHODCALLTYPE TestWriter::OnPrepareBackup(IN IVssWriterComponents *pComponents)
try
{
enterEvent(Utility::PrepareForBackup);
WriterConfiguration* config = WriterConfiguration::instance();
// get the number of components
UINT numComponents = 0;
HRESULT hr = pComponents->GetComponentCount(&numComponents);
checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
// we haven't defined CUSTOM restore method for this writer. consequently, backup apps should
// ignore it.
if ((config->restoreMethod().m_method == VSS_RME_CUSTOM) &&
numComponents > 0) {
throw Utility::TestWriterException(L"Components were selected for backup when CUSTOM restore"
L" method was used. This is incorrect");
}
m_selectedComponents.clear();
// for each component that was added
for (unsigned int x = 0; x < numComponents; x++) {
// --- get the relevant information
CComPtr<IVssComponent> pComponent;
hr = pComponents->GetComponent(x, &pComponent);
checkReturn(hr, L"IVssWriterComponents::GetComponent");
writeBackupMetadata(pComponent); // --- write private metadata
// --- find the component in the metadata document.
// --- this component may actually be a supercomponent of something
// --- listed in the metadata document, so we must handle that case.
// --- this is no longer true with the new interface changes... now, only components
// --- in the metadata doc can be added
ComponentBase identity(getPath(pComponent), getName(pComponent));
WriterConfiguration::ComponentList::iterator found =
std::find(config->components().begin(),
config->components().end(),
identity);
if (found == config->components().end()) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L" and name: " << identity.m_name << L" was added to the document" << std::endl <<
L", but does not appear in the writer metadata";
printStatus(msg.str());
} else if (!addableComponent(*found)) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L" and name: " << identity.m_name << L" was added to the document" << std::endl <<
L", but is not a selectable component";
printStatus(msg.str());
} else {
m_selectedComponents.push_back(*found);
}
}
// any non-selectable component with no selectable ancestor must be added. Check this.
vector<Component> mustAddComponents;
buildContainer_if(config->components().begin(),
config->components().end(),
std::back_inserter(mustAddComponents),
Utility::and1(std::not1(std::ptr_fun(isComponentSelectable)),
std::ptr_fun(addableComponent)));
vector<Component>::iterator currentMust = mustAddComponents.begin();
while (currentMust != mustAddComponents.end()) {
if (std::find(m_selectedComponents.begin(),
m_selectedComponents.end(),
*currentMust) == m_selectedComponents.end()) {
wstringstream msg;
msg << L"\nComponent with logical path: " << currentMust->m_logicalPath <<
L" and name: " << currentMust->m_name <<
L" is a non-selectable component with no selectable ancestor, and therefore " <<
L" must be added to the document. However, it was not added";
printStatus(msg.str());
}
++currentMust;
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in PrepareForBackup event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
};
// This function is called after a requestor calls DoSnapshotSet
// Here we ensure that the requestor has added the appropriate volumes to the
// snapshot set. If a spit directory is specified, the spit is done here as well.
bool STDMETHODCALLTYPE TestWriter::OnPrepareSnapshot()
try
{
enterEvent(Utility::PrepareForSnapshot);
// build the list of all files being backed up
vector<TargetedFile> componentFiles;
std::pointer_to_binary_function<Component, std::back_insert_iterator<vector<TargetedFile> >, void>
ptrFun(buildComponentFiles);
std::for_each(m_selectedComponents.begin(),
m_selectedComponents.end(),
std::bind2nd(ptrFun, std::back_inserter(componentFiles)));
// for every file being backed up
vector<TargetedFile>::iterator currentFile = componentFiles.begin();
while (currentFile != componentFiles.end()) {
// --- ensure the filespec has been snapshot, taking care of mount points
if (!checkPathAffected(*currentFile)) {
wstringstream msg;
msg << L"Filespec " << currentFile->m_path << currentFile->m_filespec <<
L"is selected for backup but contains files that have not been snapshot" << std::endl;
printStatus(msg.str());
}
// --- if a spit is needed, spit the file to the proper directory
if (!currentFile->m_alternatePath.empty())
spitFiles(*currentFile);
++currentFile;
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in PrepareForSnapshot event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called after a requestor calls DoSnapshotSet
// Currently, we don't do much here that is interesting.
bool STDMETHODCALLTYPE TestWriter::OnFreeze()
try
{
enterEvent(Utility::Freeze);
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in Freeze event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called after a requestor calls DoSnapshotSet
// Currently, we don't do much here that is interesting.
bool STDMETHODCALLTYPE TestWriter::OnThaw()
try
{
enterEvent(Utility::Thaw);
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in Thaw event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called after a requestor calls DoSnapshotSet
// Here we cleanup the files that were spit in OnPrepareSnapshot
// and do some basic sanity checking
bool STDMETHODCALLTYPE TestWriter::OnPostSnapshot(IN IVssWriterComponents *pComponents)
try
{
enterEvent(Utility::PostSnapshot);
cleanupFiles();
// get the number of components
UINT numComponents = 0;
HRESULT hr = pComponents->GetComponentCount(&numComponents);
checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
// for each component that was added
for (unsigned int x = 0; x < numComponents; x++) {
// --- get the relevant information
CComPtr<IVssComponent> pComponent;
hr = pComponents->GetComponent(x, &pComponent);
checkReturn(hr, L"IVssWriterComponents::GetComponent");
// --- ensure that the component was backed up
ComponentBase identity(getPath(pComponent), getName(pComponent));
vector<Component>::iterator found = std::find(m_selectedComponents.begin(),
m_selectedComponents.end(),
identity);
if (found == m_selectedComponents.end()) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L"was selected in PostSnapshot, but was not selected in PrepareForSnapshot";
printStatus(msg.str(), Utility::low);
continue;
}
if (!verifyBackupMetadata(pComponent)) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" has been corrupted in PostSnapshot";
printStatus(msg.str(), Utility::low);
}
}
m_selectedComponents.clear();
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in PostSnapshot event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called to abort the writer's backup sequence.
// If the writer has a spit component, spit files are cleaned up here.
bool STDMETHODCALLTYPE TestWriter::OnAbort()
try
{
enterEvent(Utility::Abort);
m_selectedComponents.clear();
cleanupFiles();
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in Abort event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called as a result of the requestor calling BackupComplete
// Once again we do sanity checking, and we also verify that the metadata we
// wrote in PrepareForBackup has remained the same
bool STDMETHODCALLTYPE TestWriter::OnBackupComplete(IN IVssWriterComponents *pComponents)
try
{
enterEvent(Utility::BackupComplete);
WriterConfiguration* config = WriterConfiguration::instance();
// get the number of components
UINT numComponents = 0;
HRESULT hr = pComponents->GetComponentCount(&numComponents);
checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
// for each component that was added
for (unsigned int x = 0; x < numComponents; x++) {
// --- get the relevant information
CComPtr<IVssComponent> pComponent;
hr = pComponents->GetComponent(x, &pComponent);
checkReturn(hr, L"IVssWriterComponents::GetComponent");
// --- ensure that the component is valid
ComponentBase identity(getPath(pComponent), getName(pComponent));
WriterConfiguration::ComponentList::iterator found =
std::find(config->components().begin(),
config->components().end(),
identity);
if (found == config->components().end()) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" is selected in BackupComplete, but does not appear in the writer metadata";
printStatus(msg.str(), Utility::low);
continue;
}
if (!verifyBackupMetadata(pComponent)) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" has been corrupted in BackupComplete";
printStatus(msg.str(), Utility::low);
}
// check that the backup succeeded
bool backupSucceeded = false;
hr = pComponent->GetBackupSucceeded(&backupSucceeded);
if (!backupSucceeded) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" was not marked as successfully backed up.";
}
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in BackupComplete event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called at the end of the backup process. This may happen as a result
// of the requestor shutting down, or it may happen as a result of abnormal termination
// of the requestor.
bool STDMETHODCALLTYPE TestWriter::OnBackupShutdown(IN VSS_ID SnapshotSetId)
try
{
UNREFERENCED_PARAMETER(SnapshotSetId);
enterEvent(Utility::BackupShutdown);
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in BackupShutdown event: ") + thrown.what(), Utility::low);
return false;
}
// This function is called as a result of the requestor calling PreRestore
// We check that component selection has been done properly, verify the
// backup metadata, and set targets appropriately.
bool STDMETHODCALLTYPE TestWriter::OnPreRestore(IN IVssWriterComponents *pComponents)
try
{
enterEvent(Utility::PreRestore);
WriterConfiguration* config = WriterConfiguration::instance();
// get the number of components
UINT numComponents = 0;
HRESULT hr = pComponents->GetComponentCount(&numComponents);
checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
m_selectedRestoreComponents.clear();
// for each component that was added
for (unsigned int x = 0; x < numComponents; x++) {
// --- get the relevant information
CComPtr<IVssComponent> pComponent;
hr = pComponents->GetComponent(x, &pComponent);
checkReturn(hr, L"IVssWriterComponents::GetComponent");
// --- ensure that the component is valid
ComponentBase identity(getPath(pComponent), getName(pComponent));
WriterConfiguration::ComponentList::iterator found =
std::find(config->components().begin(),
config->components().end(),
identity);
if (found == config->components().end()) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" is selected in PreRestore, but does not appear in the writer metadata";
pComponent->SetPreRestoreFailureMsg(msg.str().c_str());
printStatus(msg.str(), Utility::low);
continue;
}
// only process those component that are selected for restore
bool selectedForRestore = false;
hr = pComponent->IsSelectedForRestore(&selectedForRestore);
checkReturn(hr, L"IVssComponent::IsSelectedForRestore");
if (!selectedForRestore)
continue;
m_selectedRestoreComponents.push_back(*found);
if (!verifyBackupMetadata(pComponent)) { // --- verify the backup metadata
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" has been corrupted in PreRestore";
pComponent->SetPreRestoreFailureMsg(msg.str().c_str());
printStatus(msg.str(), Utility::low);
}
writeRestoreMetadata(pComponent); // --- write restore metadata
// --- set the target appropriately
if (found->m_restoreTarget != VSS_RT_UNDEFINED) {
HRESULT hr =pComponent->SetRestoreTarget(found->m_restoreTarget);
checkReturn(hr, L"IVssComponent::SetRestoreTarget");
printStatus(wstring(L"Set Restore Target: ") +
Utility::toString(found->m_restoreTarget), Utility::high);
}
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in PreRestore event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called as a result of the requestor calling PreRestore
// We do some sanity checking, and then check to see if files have indeed
// been restored
bool STDMETHODCALLTYPE TestWriter::OnPostRestore(IN IVssWriterComponents *pComponents)
try
{
enterEvent(Utility::PostRestore);
// get the number of components
UINT numComponents = 0;
HRESULT hr = pComponents->GetComponentCount(&numComponents);
checkReturn(hr, L"IVssWriterComponents::GetComponentCount");
// for each component
for (unsigned int x = 0; x < numComponents; x++) {
// --- get the relevant information
CComPtr<IVssComponent> pComponent;
hr = pComponents->GetComponent(x, &pComponent);
checkReturn(hr, L"I VssWriterComponents::GetComponent");
// --- ensure that the component is valid
ComponentBase identity(getPath(pComponent), getName(pComponent));
vector<Component>::iterator found = std::find(m_selectedRestoreComponents.begin(),
m_selectedRestoreComponents.end(),
identity);
if (found == m_selectedRestoreComponents.end()) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" is selected in PostRestore, but was not selected in PreRestore";
pComponent->SetPostRestoreFailureMsg(msg.str().c_str());
printStatus(msg.str(), Utility::low);
continue;
}
// only process those component that are selected for restore
bool selectedForRestore = false;
hr = pComponent->IsSelectedForRestore(&selectedForRestore);
checkReturn(hr, L"IVssComponent::IsSelectedForRestore");
if (!selectedForRestore)
continue;
if (!verifyRestoreMetadata(pComponent)) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" has been corrupted in PostRestore";
pComponent->SetPostRestoreFailureMsg(msg.str().c_str());
printStatus(msg.str(), Utility::low);
continue;
}
VSS_FILE_RESTORE_STATUS rStatus;
hr = pComponent->GetFileRestoreStatus(&rStatus);
checkReturn(hr, L"IVssComponent::GetFileRestoreStatus");
if (rStatus != VSS_RS_ALL) {
wstringstream msg;
msg << L"Component with logical path: " << identity.m_logicalPath <<
L"and name: " << identity.m_name <<
L" was not marked as having been successfully restored";
printStatus(msg.str(), Utility::low);
continue;
}
updateNewTargets(pComponent, *found);
verifyFilesRestored(pComponent, *found);
}
return true;
}
catch(const exception& thrown)
{
printStatus(string("Failure in PostRestore event: ") + thrown.what(), Utility::low);
return false;
}
catch(HRESULT error)
{
Utility::TestWriterException e(error);
printStatus(e.what(), Utility::low);
return false;
}
// This function is called at the entry to all writer events.
// A status message is printed to the console, and the event is failed if necessary.
void TestWriter::enterEvent(Utility::Events event)
{
static HRESULT errors[] = { VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT,
VSS_E_WRITERERROR_OUTOFRESOURCES,
VSS_E_WRITERERROR_TIMEOUT,
VSS_E_WRITERERROR_RETRYABLE
};
printStatus(wstring(L"\nReceived event: ") + Utility::toString(event));
WriterConfiguration* config = WriterConfiguration::instance();
// figure out whether we should fail this event
WriterEvent writerEvent(event);
WriterConfiguration::FailEventList::iterator found = std::find(config->failEvents().begin(),
config->failEvents().end(),
writerEvent);
// if so, then fail it unless failures have run out
if (found != config->failEvents().end()) {
bool failEvent = !found->m_retryable || (m_failures[event] < found->m_numFailures);
bool setFailure = inSequence(event);
if (!found->m_retryable && setFailure)
SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE);
else if (failEvent && setFailure)
SetWriterFailure(errors[rand() % (sizeof(errors) / sizeof(errors[0]))]);
if (failEvent) {
++m_failures[event];
wstringstream msg;
msg << L"Failure Requested in Event: " << Utility::toString(event) <<
L" Failing for the " << m_failures[event] << L" time";
throw Utility::TestWriterException(msg.str());
}
}
}
// This helper function adds a component to the writer-metadata document
void TestWriter::addComponent(const Component& component, IVssCreateWriterMetadata* pMetadata)
{
const wchar_t* logicalPath = component.m_logicalPath.empty() ? NULL : component.m_logicalPath.c_str();
// add the component to the document
HRESULT hr = pMetadata->AddComponent(component.m_componentType, // ct
logicalPath, // logicalwszLogicalPath
component.m_name.c_str(), // wszComponentName
NULL, // wszCaption
NULL, // pbIcon
0, // cbIcon
true, // bRestoreMetadata
true, // bNotifyOnBackupComplete
component.m_selectable, // bSelectable
component.m_selectableForRestore // bSelectableForRestore
);
checkReturn(hr, L"IVssCreateWriterMetadata::AddComponent");
printStatus(L"\nAdded component: ", Utility::high);
printStatus(component.toString(), Utility::high);
// add all of the files to the component. NOTE: we don't allow distinctions between database files
// and database log files in the VSS_CT_DATABASE case.
// we sometimes put a '\' on the end and sometimes not to keep requestors honest.
Component::ComponentFileList::iterator current = component.m_files.begin();
while (current != component.m_files.end()) {
if (component.m_componentType == VSS_CT_FILEGROUP) {
const wchar_t* alternate = current->m_alternatePath.empty() ? NULL :
current->m_alternatePath.c_str();
hr = pMetadata->AddFilesToFileGroup(logicalPath,
component.m_name.c_str(),
current->m_path.substr(0, current->m_path.size()-1).c_str(),
current->m_filespec.c_str(),
current->m_recursive,
alternate);
checkReturn(hr, L"IVssCreateWriterMetadata::AddFilesToFileGroup");
} else if (component.m_componentType == VSS_CT_DATABASE) {
hr = pMetadata->AddDatabaseFiles(logicalPath,
component.m_name.c_str(),
current->m_path.c_str(),
current->m_filespec.c_str());
checkReturn(hr, L"IVssCreateWriterMetadata::AddDatabaseFiles");
} else {
assert(false);
}
printStatus(L"\nAdded Component Filespec: ");
printStatus(current->toString());
++current;
}
// add all dependencies to the dependency list for the writer
Component::DependencyList::iterator currentDependency = component.m_dependencies.begin();
while (currentDependency != component.m_dependencies.end()) {
hr = pMetadata->AddComponentDependency(logicalPath,
component.m_name.c_str(), // wszForLogicalPath
currentDependency->m_writerId, // wszForComponentName
currentDependency->m_logicalPath.c_str(), // wszOnLogicalPath
currentDependency->m_componentName.c_str() // wszOnComponentName
);
checkReturn(hr, L"IVssCreateWriterMetadata::AddComponentDependency");
printStatus(L"\nAdded Component Dependency: ");
printStatus(currentDependency->toString());
++currentDependency;
}
}
// This helper function spits all files in a file specification to an alternate location
void TestWriter::spitFiles(const TargetedFile& file)
{
assert(!file.m_path.empty());
assert(file.m_path[file.m_path.size() - 1] == L'\\');
assert(!file.m_alternatePath.empty());
assert(file.m_alternatePath[file.m_alternatePath.size() - 1] == L'\\');
// ensure that both the source and target directories exist
DWORD attributes = ::GetFileAttributes(file.m_path.c_str());
if ((attributes == INVALID_FILE_ATTRIBUTES) ||
!(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
wstringstream msg;
msg << L"The source path " << file.m_path << L" does not exist";
throw Utility::TestWriterException(msg.str());
}
attributes = ::GetFileAttributes(file.m_alternatePath.c_str());
if ((attributes == INVALID_FILE_ATTRIBUTES) ||
!(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
wstringstream msg;
msg << L"The target path " << file.m_alternatePath << L" does not exist";
throw Utility::TestWriterException(msg.str());
}
// start by copying files from the specified root directory
std::queue<wstring> paths;
paths.push(file.m_path);
// walk through in breadth-first order. It's less resource intensive than depth-first, and
// potentially more performant
while (!paths.empty()) {
// --- grab the next path off the queue
wstring currentPath = paths.front();
paths.pop();
// --- start walking all files in the directory
WIN32_FIND_DATA findData;
Utility::AutoFindFileHandle findHandle = ::FindFirstFile((currentPath + L'*').c_str(), &findData);
if (findHandle == INVALID_HANDLE_VALUE)
continue;
do {
wstring currentName = findData.cFileName;
if (currentName == L"." ||
currentName == L"..")
continue;
std::transform(currentName.begin(), currentName.end(), currentName.begin(), towupper);
// --- if we've hit a direcctory and we care to do a recursive spit
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
file.m_recursive) {
assert(!currentName.empty());
if (currentName[currentName.size() - 1] != L'\\')
currentName += L"\\";
// figure out where the target for this new directory is
assert(currentPath.find(file.m_path) == 0);
wstring extraDirectory = currentPath.substr(file.m_path.size());
wstring alternateLocation = file.m_alternatePath + extraDirectory + currentName;
// create a target directory to hold the copied files.
if (!::CreateDirectory(alternateLocation.c_str(), NULL) &&
::GetLastError() != ERROR_ALREADY_EXISTS)
checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"CreateDirectory");
m_directoriesToRemove.push(alternateLocation.c_str());
// push the directory on the queue so it gets processed as well
paths.push(currentPath + currentName);
continue;
}
// --- if we've hit a regular file with a matching filespec
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
wildcardMatches(currentName, file.m_filespec)) {
// figure out where the new target location is
assert(currentPath.find(file.m_path) == 0);
wstring extraDirectory = currentPath.substr(file.m_path.size());
wstring alternateLocation = file.m_alternatePath + extraDirectory + currentName;
wstringstream msg;
msg << L"Spitting File: " << currentPath + currentName <<
L" To location: " << alternateLocation;
printStatus(msg.str() , Utility::high);
// copy the file over
if (!::CopyFile((currentPath + currentName).c_str(), alternateLocation.c_str(), FALSE))
checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"CopyFile");
else
m_toDelete.push_back(alternateLocation);
}
} while (::FindNextFile(findHandle, &findData));
}
}
// extract the component name from an interface pointer
wstring TestWriter::getName(IVssComponent* pComponent)
{
CComBSTR name;
HRESULT hr = pComponent->GetComponentName(&name);
checkReturn(hr, L"IVssComponent::GetComponentName");
assert(name != NULL); // this should never happen
return (BSTR)name;
}
// extract the component logical path from an interface pointer
wstring TestWriter::getPath(IVssComponent* pComponent)
{
CComBSTR path;
HRESULT hr = pComponent->GetLogicalPath(&path);
checkReturn(hr, L"IVssComponent::GetLogicalPath");
// GetLogicalPath can indeed return NULL so be careful
return (path.Length() > 0) ? (BSTR)path : L"";
}
// write a backup metadata stamp to the component
void TestWriter::writeBackupMetadata(IVssComponent* pComponent)
{
HRESULT hr = pComponent->SetBackupMetadata(metadata(pComponent, BackupString).c_str());
checkReturn(hr, L"IVssComponent::SetBackupMetadata");
printStatus(wstring(L"Writing backup metadata: ") + metadata(pComponent, BackupString),
Utility::high);
}
// verify that a backup metadata stamp is intact
bool TestWriter::verifyBackupMetadata(IVssComponent* pComponent)
{
CComBSTR data;
HRESULT hr = pComponent->GetBackupMetadata(&data);
checkReturn(hr, L"IVssComponent::GetBackupMetadata");
printStatus(wstring(L"\nComparing metadata: ") + (data.Length() ? (BSTR)data : L"") +
wstring(L" Against expected string: ") + metadata(pComponent, BackupString),
Utility::high);
if (data.Length() == 0 || metadata(pComponent, BackupString) != (BSTR)data)
return false;
return true;
}
// write a restore metadata stamp to the component
void TestWriter::writeRestoreMetadata(IVssComponent* pComponent)
{
HRESULT hr = pComponent->SetRestoreMetadata(metadata(pComponent, RestoreString).c_str());
checkReturn(hr, L"IVssComponent::SetRestoreMetadata");
printStatus(wstring(L"Writing restore metadata: ") + metadata(pComponent, RestoreString),
Utility::high);
}
// verify that a restore metadata stamp is intact
bool TestWriter::verifyRestoreMetadata(IVssComponent* pComponent)
{
CComBSTR data;
HRESULT hr = pComponent->GetRestoreMetadata(&data);
checkReturn(hr, L"IVssComponent::GetRestoreMetadata");
printStatus(wstring(L"Comparing metadata: ") + (data.Length() ? (BSTR)data : L"") +
wstring(L" Against expected string: ") + metadata(pComponent, RestoreString),
Utility::high);
if (data.Length() == 0 || metadata(pComponent, RestoreString) != (BSTR)data)
return false;
return true;
}
// check to see if the specified file (or files) are all in the current snapshot set
// doesn't check directory junctions... this will not be changed anytime soon.
// recursive mount points are also not handled very well
bool TestWriter::checkPathAffected(const TargetedFile& file)
{
wstring backupPath = file.m_alternatePath.empty() ? file.m_path : file.m_alternatePath;
// if the path in question isn't snapshot, then return false
if (!IsPathAffected(backupPath.c_str()))
return false;
// if the filespec isn't recursive, then we're done
if (!file.m_recursive)
return true;
// get the name of the volume we live on
wchar_t volumeMount[MAX_PATH];
if(!::GetVolumePathName(backupPath.c_str(), volumeMount, MAX_PATH))
checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumePathName");
assert(backupPath.find(volumeMount) == 0);
wchar_t volumeName[MAX_PATH];
if (!::GetVolumeNameForVolumeMountPoint(volumeMount, volumeName, MAX_PATH))
checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumeNameForVolumeMountPoint");
// start off with the volume name and starting directory on a worklist.
std::queue<std::pair<wstring, wstring> > worklist;
worklist.push(std::make_pair(wstring(volumeName), backupPath.substr(wcslen(volumeMount))));
while (!worklist.empty()) {
// get the current volume and directory off the worklist
wstring currentVolume = worklist.front().first;
wstring currentPath = worklist.front().second;
worklist.pop();
// now, enumerate all mount points on the volume
Utility::AutoFindMountHandle findHandle = ::FindFirstVolumeMountPoint(currentVolume.c_str(), volumeMount, MAX_PATH);
if (findHandle == INVALID_HANDLE_VALUE)
continue;
do {
std::transform(volumeMount, volumeMount + wcslen(volumeMount), volumeMount, towupper);
wstring mountPoint = currentVolume + volumeMount;
// if this mount point is included in the file specification, the volume better be included in the snapshot set
if ((mountPoint.find(currentVolume + currentPath) == 0) &&
!IsPathAffected(mountPoint.c_str()))
return false;
if (!::GetVolumeNameForVolumeMountPoint(volumeMount, volumeName, MAX_PATH))
checkReturn(HRESULT_FROM_WIN32(::GetLastError()), L"GetVolumeNameForVolumeMountPoint");
// put this volume on the worklist so it gets processed as well
// Mount points always point to the root of a volume, so pass in
// an empty second argument. When junctions are supported, we
// will pass in the target directory as the second argument.
worklist.push(std::make_pair(wstring(volumeName), wstring())); // this line will change when we support junctions
} while (::FindNextVolumeMountPoint(findHandle, volumeMount, MAX_PATH) == TRUE);
}
return true;
}
// delete all files and directories created in PrepareForSnapshot
void TestWriter::cleanupFiles()
{
// delete all created files
vector<wstring>::iterator currentFile = m_toDelete.begin();
while (currentFile != m_toDelete.end()) {
if (!::DeleteFile(currentFile->c_str()))
warnReturn(HRESULT_FROM_WIN32(::GetLastError()), L"DeleteFile");
++currentFile;
}
m_toDelete.clear();
// remove all created directories in the proper order
while (!m_directoriesToRemove.empty()) {
wstring dir = m_directoriesToRemove.top();
if (!::RemoveDirectory(dir.c_str()))
warnReturn(HRESULT_FROM_WIN32(::GetLastError()), L"RemoveDirectory");
m_directoriesToRemove.pop();
}
}
// check to see if the requestor has added any new targets, and add them to the
// Component structure
void TestWriter::updateNewTargets(IVssComponent* pComponent, Component& writerComponent)
{
HRESULT hr = S_OK;
UINT newTargetCount = 0;
hr = pComponent->GetNewTargetCount(&newTargetCount);
checkReturn(hr, L"IVssComponent::GetNewTargetCount");
writerComponent.m_newTargets.clear();
for (UINT x = 0; x < newTargetCount; x++) {
// get information about the new target
CComPtr<IVssWMFiledesc> newTarget;
hr = pComponent->GetNewTarget(x, &newTarget);
checkReturn(hr, L"IVssComponent::GetNewTarget");
CComBSTR path, filespec, alternateLocation;
bool recursive = false;
hr = newTarget->GetPath(&path);
checkReturn(hr, L"IVssComponent:GetPath");
hr = newTarget->GetFilespec(&filespec);
checkReturn(hr, L"IVssComponent:GetFilespec");
hr = newTarget->GetRecursive(&recursive);
checkReturn(hr, L"IVssComponent:GetRecursive");
hr = newTarget->GetAlternateLocation(&alternateLocation);
checkReturn(hr, L"IVssComponent:GetAlternateLocation");
// add it to the new-target list
writerComponent.m_newTargets.push_back(TargetedFile(wstring(path),
wstring(filespec),
recursive,
wstring(alternateLocation)));
}
}
// verify that files in the component were restored properly.
// assumption is that the directory being restored to is empty if the checkExcluded parameter is true.
// currently, we have a very simple-minded approach to handle the wildcard case
// a more general solution will involve hashing files, and will be implemented if time is found
void TestWriter::verifyFilesRestored(IVssComponent* pComponent, const Component& writerComponent)
{
WriterConfiguration* config = WriterConfiguration::instance();
// no checking is being done. Don't do anything
if (!config->checkIncludes() && !config->checkExcludes())
return;
// for each file in the component
VSS_RESTORE_TARGET target = writerComponent.m_restoreTarget;
VSS_RESTOREMETHOD_ENUM method = config->restoreMethod().m_method;
// build the list of all filespecs that need restoring
vector<TargetedFile> componentFiles;
buildComponentFiles(writerComponent, std::back_inserter(componentFiles));
for (vector<TargetedFile>::iterator currentFile = componentFiles.begin();
currentFile != componentFiles.end();
++currentFile) {
// --- figure out if there are any matching exclude files
vector<File> excludeFiles;
if (config->checkExcludes()) {
buildContainer_if(config->excludeFiles().begin(),
config->excludeFiles().end(),
std::back_inserter(excludeFiles),
std::bind2nd(std::ptr_fun(targetMatches), *currentFile));
}
// if there's no checking to be done for this filespec, continue
if (excludeFiles.empty() && !config->checkIncludes())
continue;
// --- if there are new targets, look for one that references our file
// --- if we find such a target, ensure that the file was restored there
// --- NOTE: after the interface changes, there should be at most one matching
// --- target.
vector<TargetedFile> targets;
buildContainer_if(writerComponent.m_newTargets.begin(),
writerComponent.m_newTargets.end(),
std::back_inserter(targets),
std::bind2nd(std::equal_to<File>(), *currentFile));
if (targets.size() > 1) {
wstringstream msg;
msg << L"More than one new target matched filespec " <<
currentFile->toString() << std::endl << L"This is an illegal configuration";
printStatus(msg.str());
}
if (!targets.empty()) {
// create a function object to use for verification
VerifyFileAtLocation locationChecker(excludeFiles, pComponent, false);
locationChecker(targets[0], *currentFile); // TODO: no longer need this fancy functor, since we're not doing a for_each
}
vector<TargetedFile> alternateLocations;
buildContainer_if(config->restoreMethod().m_alternateLocations.begin(),
config->restoreMethod().m_alternateLocations.end(),
std::back_inserter(alternateLocations),
std::bind2nd(std::equal_to<File>(), *currentFile));
// --- NOTE: once again, interface changes mean we expect at most one
assert(alternateLocations.size() <= 1);
bool alternateRestore = !alternateLocations.empty() &&
((target == VSS_RT_ALTERNATE) || (method == VSS_RME_RESTORE_TO_ALTERNATE_LOCATION));
if ((method == VSS_RME_RESTORE_IF_CAN_REPLACE) ||
(method == VSS_RME_RESTORE_IF_NOT_THERE) ||
alternateRestore) {
// --- in all of these cases, the backup application may restore to an alternate location.
// if we're not in either of the following two states, then the alternate location should only be used if there's
// a matching element in the backup document. Check to see if this is true
// create a function object to use for verification
// TODO: we no longer need this fancy functor object since we're not doing a for_each
if (!alternateLocations.empty()) {
VerifyFileAtLocation locationChecker(excludeFiles, pComponent,
(target != VSS_RT_ALTERNATE) && (method != VSS_RME_RESTORE_TO_ALTERNATE_LOCATION));
// check to ensure that the file has been restored to each matching alternate location
// once again, this isn't quite correct, but good enough for now. More complicated
// test scenarios will eventually break this.
locationChecker(alternateLocations[0], *currentFile);
}
}
// none of the above cases are true. We need to check to see that the file is restored to its original location
if ((method != VSS_RME_RESTORE_AT_REBOOT) && (method != VSS_RME_RESTORE_AT_REBOOT_IF_CANNOT_REPLACE) &&
!alternateRestore) {
// create a function object to use for verification
VerifyFileAtLocation locationChecker(excludeFiles, pComponent, false);
locationChecker(TargetedFile(currentFile->m_path,
currentFile->m_filespec,
currentFile->m_recursive,
currentFile->m_path),
*currentFile);
}
}
}
bool __cdecl TestWriter::isSubcomponent(ComponentBase sub, ComponentBase super)
{
// if the components are the same, then return true
if (super == sub)
return true;
wstring path = super.m_logicalPath;
if (!path.empty() && path[path.size() - 1] != L'\\')
path+= L"\\";
path += super.m_name;
// if the supercomponent full path is the same as the subcomponent logical path, then true
if (path == sub.m_logicalPath)
return true;
// otherwise, check for partial match
return sub.m_logicalPath.find(path + L"\\") == 0;
}
bool __cdecl TestWriter::targetMatches (File target, File file)
{
assert(!file.m_filespec.empty());
assert(!target.m_filespec.empty());
// the filespec must match first of all
if (!wildcardMatches(file.m_filespec, target.m_filespec))
return false;
// check the path
if (file.m_recursive) {
if (!target.m_recursive)
return target.m_path.find(file.m_path) == 0;
else
return (target.m_path.find(file.m_path) == 0) ||(file.m_path.find(target.m_path) == 0);
} else {
if (!target.m_recursive)
return file.m_path == target.m_path;
else
return file.m_path.find(target.m_path) == 0;
}
}
// This helper function tests whether a component can be legally added to the backup document
bool __cdecl TestWriter::addableComponent(Component toAdd)
{
WriterConfiguration* config = WriterConfiguration::instance();
if (toAdd.m_selectable)
return true;
// see if there are any selectable ancestors
vector<Component> ancestors;
buildContainer_if(config->components().begin(),
config->components().end(),
std::back_inserter(ancestors),
Utility::and1(std::bind2nd(std::ptr_fun(isSupercomponent), toAdd),
std::ptr_fun(isComponentSelectable)));
return ancestors.empty();
}
// check to see if two wildcards match.
// specifically, check to see whether the set of expansions of the first wildcard has a
// non-empty intersection with the set of expansions of the second wildcard.
// This function is not terribly efficient, but wildcards tend to be fairly short.
bool TestWriter::wildcardMatches(const wstring& first, const wstring& second)
{
// if both string are empty, then they surely match
if (first.empty() && second.empty())
return true;
// if we're done with the component, the wildcard better be terminated with '*' characters
if (first.empty())
return (second[0] == L'*') && wildcardMatches(first, second.substr(1));
if (second.empty())
return (first[0] == L'*') && wildcardMatches(first.substr(1), second);
switch(first[0]) {
case L'?':
if (second[0] == L'*') {
return wildcardMatches(first.substr(1), second) || // '*' matches character
wildcardMatches(first, second.substr(1)); // '*' matches nothing
}
// otherwise, the rest of the strings must match
return wildcardMatches(first.substr(1), second.substr(1));
case L'*':
return wildcardMatches(first, second.substr(1)) || // '*' matches character
wildcardMatches(first.substr(1), second); // '*' matches nothing
default:
switch(second[0]) {
case L'?':
return wildcardMatches(first.substr(1), second.substr(1));
case L'*':
return wildcardMatches(first.substr(1), second) || // '*' matches character
wildcardMatches(first, second.substr(1)); // '*' matches nothing
default:
return (first[0] == second[0]) &&
wildcardMatches(first.substr(1), second.substr(1));
}
}
}
wstring TestWriter::VerifyFileAtLocation::verifyFileAtLocation(const File& file, const TargetedFile& location) const
{
WriterConfiguration* config = WriterConfiguration::instance();
// complicated set of assertions.
assert(!(file.m_recursive && !location.m_recursive) ||
(location.m_path.find(file.m_path) == 0));
assert(!(location.m_recursive && !file.m_recursive) ||
(file.m_path.find(location.m_path) == 0));
assert(!(file.m_recursive && location.m_recursive) ||
((file.m_path.find(location.m_path) == 0) || (location.m_path.find(file.m_path) == 0)));
assert(!m_excluded.empty() || config->checkIncludes());
assert(m_excluded.empty() || config->checkExcludes());
// performant case where we don't have to walk any directory trees
if (!file.m_recursive && !location.m_recursive && isExact(file.m_filespec)) {
assert(m_excluded.size() <= 1); // if not, the config file isn't set up right
// --- if this is an alternate location mapping, only process it if there's a matching alternate location
// --- in the backup document
if (m_verifyAlternateLocation &&
!verifyAlternateLocation(TargetedFile(file.m_path, file.m_filespec, false, location.m_alternatePath))) {
return L"";
}
// --- ensure that the file has been restored, unless the file is excluded
printStatus(wstring(L"\nChecking file ") +
location.m_alternatePath + file.m_filespec,
Utility::high);
// check for error cases
if (m_excluded.empty()) {
if (::GetFileAttributes((location.m_alternatePath + file.m_filespec).c_str()) == INVALID_FILE_ATTRIBUTES) {
wstringstream msg;
msg << L"\nThe file: " << std::endl << file.toString() << std::endl <<
L"was not restored to location " << location.m_alternatePath;
printStatus(msg.str(), Utility::low);
return msg.str();
}
} else if (::GetFileAttributes((location.m_alternatePath + file.m_filespec).c_str()) != INVALID_FILE_ATTRIBUTES) {
wstringstream msg;
msg << L"\nThe file: " << file.m_path << file.m_filespec <<
L" should have been excluded, but appears in location " << location.m_alternatePath;
printStatus(msg.str(), Utility::low);
return msg.str();
}
return L"";
}
std::queue<wstring> paths;
// figure out what directory to start looking from
wstring startPath = location.m_alternatePath;
if (location.m_recursive && (file.m_path.find(location.m_path) == 0))
startPath += file.m_path.substr(location.m_path.size());
paths.push(startPath);
// in the recursive case, files will hopefully be backed up high in the directory tree
// consequently, we're going to walk the tree breadth-first
printStatus(L"\nChecking that filespec was restored:", Utility::high);
while (!paths.empty()) {
wstring currentPath = paths.front();
paths.pop();
printStatus(wstring(L" Checking directory: ") + currentPath,
Utility::high);
// for every file in the current directory (can't pass in filespec since we want to match all directories)
WIN32_FIND_DATA findData;
Utility::AutoFindFileHandle findHandle = ::FindFirstFile((currentPath + L"*").c_str(), &findData);
if (findHandle == INVALID_HANDLE_VALUE)
continue;
do {
wstring currentName = findData.cFileName;
std::transform(currentName.begin(), currentName.end(), currentName.begin(), towupper);
if (currentName == L"." ||
currentName == L"..")
continue;
// --- if the file is a directory
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
assert(!currentName.empty());
if (currentName[currentName.size() - 1] != L'\\')
currentName += L"\\";
// add it if necessary
if (file.m_recursive)
paths.push(currentPath + currentName);
continue; // skip to next file
}
printStatus(wstring(L" Checking file: ") + currentName);
// --- translate the path to what it would have been in the original tree
assert(currentPath.find(location.m_alternatePath) == 0);
wstring originalPath = file.m_path;
if (file.m_recursive && (location.m_path.find(file.m_path) == 0))
originalPath += location.m_path.substr(file.m_path.size());
originalPath += currentPath.substr(location.m_alternatePath.size());
// --- if this is an alternate location mapping, only process it if there's a matching
// --- alternate location mapping in the backup document
if (m_verifyAlternateLocation &&
!verifyAlternateLocation(TargetedFile(originalPath, currentName, false, currentPath))) {
continue;
}
// --- find an exclude item that matches
// --- if !config->checkExcluded(), m_excluded will be an empty container, and
// --- std::find_if will return the end iterator
vector<File>::const_iterator found =
std::find_if(m_excluded.begin(),
m_excluded.end(),
std::bind2nd(std::ptr_fun(targetMatches), File(originalPath, currentName, false)));
// --- return if this is either an excluded file, or if we've found at least one matching include file
if (found != m_excluded.end()) {
wstringstream msg;
msg << L"The file " << originalPath << currentName <<
L" should have been excluded, but appears in location " << currentPath;
printStatus(msg.str(), Utility::low);
return msg.str();
} else if (config->checkIncludes() &&
wildcardMatches(currentName, file.m_filespec)) {
return L""; // declare success in cheesy case
}
} while (::FindNextFile(findHandle, &findData));
}
if (config->checkIncludes()) {
wstringstream msg;
msg << L"None of the files specified by " << std::endl << file.toString() << std::endl <<
L" were restored to location " << location.m_alternatePath;
printStatus(msg.str(), Utility::low);
return msg.str();
}
// we're only checking excludes, and we didn't find any violations
return L"";
}
// verify that an alternate location mapping appears in the backup document
bool TestWriter::VerifyFileAtLocation::verifyAlternateLocation(const TargetedFile& writerAlt) const
{
assert (isExact(writerAlt.m_filespec));
assert(!writerAlt.m_recursive);
unsigned int mappings = 0;
HRESULT hr = m_pComponent->GetAlternateLocationMappingCount(&mappings);
checkReturn(hr, L"IVssComponent::GetAlternateLocationMappingCount");
for (unsigned int x = 0; x < mappings; x++) {
// get the current alternate location mapping
CComPtr<IVssWMFiledesc> filedesc;
hr = m_pComponent->GetAlternateLocationMapping(x, &filedesc);
checkReturn(hr, L"IVssComponent::GetAlternateLocationMapping");
// grab all relevant fields
CComBSTR bstrPath, bstrFilespec, bstrAlternateLocation;
hr = filedesc->GetPath(&bstrPath);
checkReturn(hr, L"IVssComponent::GetPath");
if (bstrPath.Length() == 0) {
printStatus(L"An Alternate Location Mapping with an empty path was added to the backup document",
Utility::low);
continue;
}
hr = filedesc->GetFilespec(&bstrFilespec);
checkReturn(hr, L"IVssComponent::GetFilespec");
if (bstrFilespec.Length() == 0) {
printStatus(L"An Alternate Location Mapping with an empty filespec was added to the backup document",
Utility::low);
continue;
}
hr = filedesc->GetAlternateLocation(&bstrAlternateLocation);
checkReturn(hr, L"IVssComponent::GetAlternateLocation");
if (bstrAlternateLocation.Length() == 0) {
printStatus(L"An Alternate Location Mapping with an empty alternateLocation was added to the backup document",
Utility::low);
continue;
}
bool recursive;
hr = filedesc->GetRecursive(&recursive);
checkReturn(hr, L"IVssComponent::GetRecursive");
// convert the fields to uppercase and ensure paths are '\' terminated
wstring path = bstrPath;
std::transform(path.begin(), path.end(), path.begin(), towupper);
if (path[path.size() - 1] != L'\\')
path += L'\\';
wstring filespec = bstrFilespec;
std::transform(filespec.begin(), filespec.end(), filespec.begin(), towupper);
wstring alternatePath = bstrAlternateLocation;
std::transform(alternatePath.begin(), alternatePath.end(), alternatePath.begin(), towupper);
if (alternatePath[alternatePath.size() - 1] != L'\\')
alternatePath += L'\\';
// check to see if that passed-in mapping is encompassed by the one in the backup document
if (targetMatches(File(path, filespec, recursive), writerAlt)) {
if (recursive) {
if (writerAlt.m_alternatePath.find(alternatePath) != 0)
return false;
assert(writerAlt.m_path.find(path) == 0);
alternatePath += writerAlt.m_path.substr(path.size());
}
return alternatePath == writerAlt.m_alternatePath;
}
}
return false;
}
// add the current error message to the PostRestoreFailureMsg
void TestWriter::VerifyFileAtLocation::saveErrorMessage(const wstring& message) const
{
if (!message.empty()) {
CComBSTR old;
m_pComponent->GetPostRestoreFailureMsg(&old);
wstring oldMessage = (old.Length() > 0) ? (BSTR)old : L"";
m_pComponent->SetPostRestoreFailureMsg((oldMessage + wstring(L"\n") + message).c_str());
}
}