2025-11-28 00:35:46 +09:00

773 lines
28 KiB
C++

/////////////////////////////////////////////////////////////////////////
// Copyright © 2006 Microsoft Corporation. All rights reserved.
//
// This file may contain preliminary information or inaccuracies,
// and may not correctly represent any associated Microsoft
// Product as commercially released. All Materials are provided entirely
// “AS IS.” To the extent permitted by law, MICROSOFT MAKES NO
// WARRANTY OF ANY KIND, DISCLAIMS ALL EXPRESS, IMPLIED AND STATUTORY
// WARRANTIES, AND ASSUMES NO LIABILITY TO YOU FOR ANY DAMAGES OF
// ANY TYPE IN CONNECTION WITH THESE MATERIALS OR ANY INTELLECTUAL PROPERTY IN THEM.
//
// Main header
#include "stdafx.h"
////////////////////////////////////////////////////////////////////////////////////
// Main routines for writer component selection
//
// Select the maximum number of components such that their
// file descriptors are pointing only to volumes to be shadow copied
void VssClient::SelectComponentsForBackup(
vector<wstring> shadowSourceVolumes,
vector<wstring> excludedWriterAndComponentList,
vector<wstring> includedWriterAndComponentList
)
{
FunctionTracer ft(DBG_INFO);
// First, exclude all components that have data outside of the shadow set
DiscoverDirectlyExcludedComponents(excludedWriterAndComponentList, m_writerList);
// Then discover excluded components that have file groups outside the shadow set
DiscoverNonShadowedExcludedComponents(shadowSourceVolumes);
// Now, exclude all componenets that are have directly excluded descendents
DiscoverAllExcludedComponents();
// Next, exclude all writers that:
// - either have a top-level nonselectable excluded component
// - or do not have any included components (all its components are excluded)
DiscoverExcludedWriters();
// Now, discover the components that should be included (explicitly or implicitly)
// These are the top components that do not have any excluded children
DiscoverExplicitelyIncludedComponents();
// Verify if the specified writers/components were included
ft.WriteLine(L"Verifying explicitly specified writers/components ...");
for(unsigned i = 0; i < includedWriterAndComponentList.size(); i++)
{
// Check whether a component or a writer is specified
if (includedWriterAndComponentList[i].find(L':') != wstring::npos)
VerifyExplicitelyIncludedComponent(includedWriterAndComponentList[i], m_writerList);
else
VerifyExplicitelyIncludedWriter(includedWriterAndComponentList[i], m_writerList);
}
// Finally, select the explicitly included components
SelectExplicitelyIncludedComponents();
}
// Select the maximum number of components such that their
// file descriptors are pointing only to volumes to be shadow copied
void VssClient::SelectComponentsForRestore(
vector<wstring> excludedWriterAndComponentList,
vector<wstring> includedWriterAndComponentList
)
{
FunctionTracer ft(DBG_INFO);
// First, exclude all components that have data outside of the shadow set
DiscoverDirectlyExcludedComponents(excludedWriterAndComponentList, m_writerComponentsForRestore);
// Exclude all writers that do not support restore events
ExcludeWritersWithNoRestoreEvents();
// Verify if the specified writers/components were included
ft.WriteLine(L"Verifying explicitly specified writers/components ...");
for(unsigned i = 0; i < includedWriterAndComponentList.size(); i++)
{
// Check whether a component or a writer is specified
if (includedWriterAndComponentList[i].find(L':') != wstring::npos)
VerifyExplicitelyIncludedComponent(includedWriterAndComponentList[i], m_writerComponentsForRestore);
else
VerifyExplicitelyIncludedWriter(includedWriterAndComponentList[i], m_writerComponentsForRestore);
}
// Finally, select the explicitly included components
SelectNonexcludedComponentsForRestore();
}
// Discover directly excluded components (that were excluded through the command-line)
void VssClient::DiscoverDirectlyExcludedComponents(
vector<wstring> excludedWriterAndComponentList,
vector<VssWriter> & writerList
)
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Discover directly excluded components ...");
// Discover components that should be excluded from the shadow set
// This means components that have at least one File Descriptor requiring
// volumes not in the shadow set.
for (unsigned iWriter = 0; iWriter < writerList.size(); iWriter++)
{
VssWriter & writer = writerList[iWriter];
// Check if the writer is excluded
if (FindStringInList(writer.name, excludedWriterAndComponentList) ||
FindStringInList(writer.id, excludedWriterAndComponentList) ||
FindStringInList(writer.instanceId, excludedWriterAndComponentList))
{
writer.isExcluded = true;
continue;
}
// Check if the component is excluded
for (unsigned iComponent = 0; iComponent < writer.components.size(); iComponent++)
{
VssComponent & component = writer.components[iComponent];
// Check to see if this component is explicitly excluded
// Compute various component paths
// Format: Writer:logicaPath\componentName
wstring componentPathWithWriterName = writer.name + L":" + component.fullPath;
wstring componentPathWithWriterID = writer.id + L":" + component.fullPath;
wstring componentPathWithWriterIID = writer.instanceId + L":" + component.fullPath;
// Check to see if this component is explicitly excluded
if (FindStringInList(componentPathWithWriterName, excludedWriterAndComponentList) ||
FindStringInList(componentPathWithWriterID, excludedWriterAndComponentList) ||
FindStringInList(componentPathWithWriterIID, excludedWriterAndComponentList))
{
ft.WriteLine(L"- Component '%s' from writer '%s' is explicitly excluded from backup ",
component.fullPath.c_str(), writer.name.c_str());
component.isExcluded = true;
continue;
}
}
// Now, discover if we have any selected components. If none, exclude the whole writer
bool nonExcludedComponents = false;
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
if (!component.isExcluded)
nonExcludedComponents = true;
}
// If all components are missing or excluded, then exclude the writer too
if (!nonExcludedComponents)
{
ft.WriteLine(L"- Excluding writer '%s' since it has no selected components for restore.", writer.name.c_str());
writer.isExcluded = true;
}
}
}
// Exclude writers that do not support restore events
void VssClient::ExcludeWritersWithNoRestoreEvents()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Exclude writers that do not support restore events ...");
for (unsigned iWriter = 0; iWriter < m_writerComponentsForRestore.size(); iWriter++)
{
VssWriter & writer = m_writerComponentsForRestore[iWriter];
// Check if the writer is excluded
if (writer.isExcluded)
continue;
if (!writer.supportsRestore)
{
ft.WriteLine(L"- Excluding writer '%s' since it does not support restore events.", writer.name.c_str());
writer.isExcluded = true;
}
}
}
// Discover excluded components that have file groups outside the shadow set
void VssClient::DiscoverNonShadowedExcludedComponents(
vector<wstring> shadowSourceVolumes
)
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Discover components that reside outside the shadow set ...");
// Discover components that should be excluded from the shadow set
// This means components that have at least one File Descriptor requiring
// volumes not in the shadow set.
for (unsigned iWriter = 0; iWriter < m_writerList.size(); iWriter++)
{
VssWriter & writer = m_writerList[iWriter];
// Check if the writer is excluded
if (writer.isExcluded)
continue;
// Check if the component is excluded
for (unsigned iComponent = 0; iComponent < writer.components.size(); iComponent++)
{
VssComponent & component = writer.components[iComponent];
// Check to see if this component is explicitly excluded
if (component.isExcluded)
continue;
// Try to find an affected volume outside the shadow set
// If yes, exclude the component
for (unsigned iVol = 0; iVol < component.affectedVolumes.size(); iVol++)
{
if (ClusterIsPathOnSharedVolume(component.affectedVolumes[iVol].c_str()))
{
wstring wsUniquePath(MAX_PATH, L'\0');
ClusterGetVolumeNameForVolumeMountPoint(component.affectedVolumes[iVol].c_str(),
WString2Buffer(wsUniquePath),
(DWORD)wsUniquePath.length());
component.affectedVolumes[iVol] = wsUniquePath;
}
if (!FindStringInList(component.affectedVolumes[iVol], shadowSourceVolumes))
{
wstring wsLocalVolume;
if (GetDisplayNameForVolumeNoThrow(component.affectedVolumes[iVol], wsLocalVolume))
ft.WriteLine(L"- Component '%s' from writer '%s' is excluded from backup (it requires %s in the shadow set)",
component.fullPath.c_str(), writer.name.c_str(), wsLocalVolume.c_str());
else
ft.WriteLine(L"- Component '%s' from writer '%s' is excluded from backup", component.fullPath.c_str(), writer.name.c_str());
component.isExcluded = true;
break;
}
}
}
}
}
// Discover the components that should not be included (explicitly or implicitly)
// These are componenets that are have directly excluded descendents
void VssClient::DiscoverAllExcludedComponents()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Discover all excluded components ...");
// Discover components that should be excluded from the shadow set
// This means components that have at least one File Descriptor requiring
// volumes not in the shadow set.
for (unsigned iWriter = 0; iWriter < m_writerList.size(); iWriter++)
{
VssWriter & writer = m_writerList[iWriter];
if (writer.isExcluded)
continue;
// Enumerate all components
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
// Check if this component has any excluded children
// If yes, deselect it
for (unsigned j = 0; j < writer.components.size(); j++)
{
VssComponent & descendent = writer.components[j];
if (component.IsAncestorOf(descendent) && descendent.isExcluded)
{
ft.WriteLine(L"- Component '%s' from writer '%s' is excluded from backup "
L"(it has an excluded descendent: '%s')",
component.fullPath.c_str(), writer.name.c_str(), descendent.name.c_str());
component.isExcluded = true;
break;
}
}
}
}
}
// Discover excluded writers. These are writers that:
// - either have a top-level nonselectable excluded component
// - or do not have any included components (all its components are excluded)
void VssClient::DiscoverExcludedWriters()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Discover excluded writers ...");
// Enumerate writers
for (unsigned iWriter = 0; iWriter < m_writerList.size(); iWriter++)
{
VssWriter & writer = m_writerList[iWriter];
if (writer.isExcluded)
continue;
// Discover if we have any:
// - non-excluded selectable components
// - or non-excluded top-level non-selectable components
// If we have none, then the whole writer must be excluded from the backup
writer.isExcluded = true;
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
if (component.CanBeExplicitlyIncluded())
{
writer.isExcluded = false;
break;
}
}
// No included components were found
if (writer.isExcluded)
{
ft.WriteLine(L"- The writer '%s' is now entirely excluded from the backup:", writer.name.c_str());
ft.WriteLine(L" (it does not contain any components that can be potentially included in the backup)");
continue;
}
// Now, discover if we have any top-level excluded non-selectable component
// If this is true, then the whole writer must be excluded from the backup
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
if (component.isTopLevel && !component.isSelectable && component.isExcluded)
{
ft.WriteLine(L"- The writer '%s' is now entirely excluded from the backup:", writer.name.c_str());
ft.WriteLine(L" (the top-level non-selectable component '%s' is an excluded component)",
component.fullPath.c_str());
writer.isExcluded = true;
break;
}
}
}
}
// Discover the components that should be explicitly included
// These are any included top components
void VssClient::DiscoverExplicitelyIncludedComponents()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Discover explicitly included components ...");
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < m_writerList.size(); iWriter++)
{
VssWriter & writer = m_writerList[iWriter];
if (writer.isExcluded)
continue;
// Compute the roots of included components
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
if (!component.CanBeExplicitlyIncluded())
continue;
// Test if our component has a parent that is also included
component.isExplicitlyIncluded = true;
for (unsigned j = 0; j < writer.components.size(); j++)
{
VssComponent & ancestor = writer.components[j];
if (ancestor.IsAncestorOf(component) && ancestor.CanBeExplicitlyIncluded())
{
// This cannot be explicitely included since we have another
// ancestor that that must be (implictely or explicitely) included
component.isExplicitlyIncluded = false;
break;
}
}
}
}
}
// Verify that the given components will be explicitly or implicitly selected
void VssClient::VerifyExplicitelyIncludedComponent(
wstring includedComponent,
vector<VssWriter> & writerList
)
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"- Verifing component \"%s\" ...", includedComponent.c_str());
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < writerList.size(); iWriter++)
{
VssWriter & writer = writerList[iWriter];
// Ignore explicitly excluded writers
if (writer.isExcluded)
continue;
// Find the associated component
for (unsigned j = 0; j < writer.components.size(); j++)
{
VssComponent & component = writer.components[j];
// Ignore explicitly excluded components
if (component.isExcluded)
continue;
// Compute various component paths
// Format: Writer:logicaPath\componentName
wstring componentPathWithWriterName = writer.name + L":" + component.fullPath;
wstring componentPathWithWriterID = writer.id + L":" + component.fullPath;
wstring componentPathWithWriterIID = writer.instanceId + L":" + component.fullPath;
// Check to see if this component is (implicitly or explicitly) included
if (IsEqual(componentPathWithWriterName, includedComponent) ||
IsEqual(componentPathWithWriterID, includedComponent) ||
IsEqual(componentPathWithWriterIID, includedComponent))
{
ft.Trace(DBG_INFO, L"- Found component '%s' from writer '%s'",
component.fullPath.c_str(), writer.name.c_str());
// If we are during restore, we just found our component
if (m_bDuringRestore)
{
ft.WriteLine(L" - The component \"%s\" is selected", includedComponent.c_str());
return;
}
// If not explicitly included, check to see if there is an explicitly included ancestor
bool isIncluded = component.isExplicitlyIncluded;
if (!isIncluded)
{
for (unsigned k = 0; k < writer.components.size(); k++)
{
VssComponent & ancestor = writer.components[k];
if (ancestor.IsAncestorOf(component) && ancestor.isExplicitlyIncluded)
{
isIncluded = true;
break;
}
}
}
if (isIncluded)
{
ft.WriteLine(L" - The component \"%s\" is selected", includedComponent.c_str());
return;
}
else
{
ft.WriteLine(L"ERROR: The component \"%s\" was not included in the backup! Aborting backup ...", includedComponent.c_str());
ft.WriteLine(L"- Please reveiw the component/subcomponent definitions");
ft.WriteLine(L"- Also, please verify list of volumes to be shadow copied.");
throw(E_INVALIDARG);
}
}
}
}
ft.WriteLine(L"ERROR: The component \"%s\" was not found in the writer components list! Aborting backup ...", includedComponent.c_str());
ft.WriteLine(L"- Please check the syntax of the component name.");
throw(E_INVALIDARG);
}
// Verify that all the components of this writer are selected
void VssClient::VerifyExplicitelyIncludedWriter(
wstring writerName,
vector<VssWriter> & writerList
)
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"- Verifing that all components of writer \"%s\" are included in backup ...", writerName.c_str());
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < writerList.size(); iWriter++)
{
VssWriter & writer = writerList[iWriter];
// Ignore explicitly excluded writers
if (writer.isExcluded)
continue;
// Check if we found the writer
if (IsEqual(writerName, writer.name)
|| IsEqual(writerName, writer.id)
|| IsEqual(writerName, writer.instanceId))
{
if (writer.isExcluded)
{
ft.WriteLine(L"ERROR: The writer \"%s\" was not included in the backup! Aborting backup ...", writer.name.c_str());
ft.WriteLine(L"- Please reveiw the component/subcomponent definitions");
ft.WriteLine(L"- Also, please verify list of volumes to be shadow copied.");
throw(E_INVALIDARG);
}
// Make sure all its associated components are selected
for (unsigned j = 0; j < writer.components.size(); j++)
{
VssComponent & component = writer.components[j];
if (component.isExcluded)
{
ft.WriteLine(L"ERROR: The writer \"%s\" has components not included in the backup! Aborting backup ...", writer.name.c_str());
ft.WriteLine(L"- The component \"%s\" was not included in the backup.", component.fullPath.c_str());
ft.WriteLine(L"- Please reveiw the component/subcomponent definitions");
ft.WriteLine(L"- Also, please verify list of volumes to be shadow copied.");
throw(E_INVALIDARG);
}
}
ft.WriteLine(L" - All components from writer \"%s\" are selected", writerName.c_str());
return;
}
}
ft.WriteLine(L"ERROR: The writer \"%s\" was not found! Aborting backup ...", writerName.c_str());
ft.WriteLine(L"- Please check the syntax of the writer name/id.");
throw(E_INVALIDARG);
}
// Discover the components that should be explicitly included
// These are any included top components
void VssClient::SelectExplicitelyIncludedComponents()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Select explicitly included components ...");
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < m_writerList.size(); iWriter++)
{
VssWriter & writer = m_writerList[iWriter];
if (writer.isExcluded)
continue;
ft.WriteLine(L" * Writer '%s':", writer.name.c_str());
// Compute the roots of included components
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
if (!component.isExplicitlyIncluded)
continue;
ft.WriteLine(L" - Add component %s", component.fullPath.c_str());
// Add the component
CHECK_COM(m_pVssObject->AddComponent(
WString2Guid(writer.instanceId),
WString2Guid(writer.id),
component.type,
component.logicalPath.c_str(),
component.name.c_str()));
}
}
}
// Select non excluded components for restore
void VssClient::SelectNonexcludedComponentsForRestore()
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Select components for restore...");
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < m_writerComponentsForRestore.size(); iWriter++)
{
VssWriter & writer = m_writerComponentsForRestore[iWriter];
// Ignore explicitly excluded writers
if (writer.isExcluded)
continue;
ft.WriteLine(L" * Writer '%s':", writer.name.c_str());
// Compute the roots of included components
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
// Do not select excluded components
if (component.isExcluded)
continue;
ft.WriteLine(L" - Select component %s", component.fullPath.c_str());
// Select the component for restore
CHECK_COM(m_pVssObject->SetSelectedForRestore(
WString2Guid(writer.id),
component.type,
component.logicalPath.c_str(),
component.name.c_str(),
true));
}
}
}
// Notify the writer on the restore status
void VssClient::SetFileRestoreStatus(bool bSuccesfullyRestored)
{
FunctionTracer ft(DBG_INFO);
ft.WriteLine(L"Set restore status for all components components for restore...");
//
// All-or-nothing policy
//
// WARNING: this might be insufficient since we cannot distinguish
// between a partial failed restore and a completely failed restore!
// A true requestor should be able to make this difference (see the documentation for more details)
VSS_FILE_RESTORE_STATUS restoreStatus = bSuccesfullyRestored? VSS_RS_ALL: VSS_RS_NONE;
// Enumerate all writers
for (unsigned iWriter = 0; iWriter < m_writerComponentsForRestore.size(); iWriter++)
{
VssWriter & writer = m_writerComponentsForRestore[iWriter];
if (writer.isExcluded)
continue;
ft.WriteLine(L" * Writer '%s':", writer.name.c_str());
// Compute the roots of included components
for (unsigned i = 0; i < writer.components.size(); i++)
{
VssComponent & component = writer.components[i];
// Do not select excluded components
if (component.isExcluded)
continue;
ft.WriteLine(L" - Select component %s", component.fullPath.c_str());
// Select the component for restore
CHECK_COM(m_pVssObject->SetFileRestoreStatus(
WString2Guid(writer.id),
component.type,
component.logicalPath.c_str(),
component.name.c_str(),
restoreStatus)
);
}
}
}
// Returns TRUE if the writer was previously selected
bool VssClient::IsWriterSelected(GUID guidInstanceId)
{
// If this writer was not selected for backup, ignore it
wstring instanceId = Guid2WString(guidInstanceId);
for (unsigned i = 0; i < m_writerList.size(); i++)
if ( (instanceId == m_writerList[i].instanceId) && !m_writerList[i].isExcluded)
return true;
return false;
}
// Check the status for all selected writers
void VssClient::CheckSelectedWriterStatus()
{
FunctionTracer ft(DBG_INFO);
if ((m_dwContext & VSS_VOLSNAP_ATTR_NO_WRITERS) != 0)
return;
// Gather writer status to detect potential errors
GatherWriterStatus();
// Gets the number of writers in the gathered status info
// (WARNING: GatherWriterStatus must be called before)
unsigned cWriters = 0;
CHECK_COM(m_pVssObject->GetWriterStatusCount(&cWriters));
// Enumerate each writer
for(unsigned iWriter = 0; iWriter < cWriters; iWriter++)
{
VSS_ID idInstance = GUID_NULL;
VSS_ID idWriter= GUID_NULL;
VSS_WRITER_STATE eWriterStatus = VSS_WS_UNKNOWN;
CComBSTR bstrWriterName;
HRESULT hrWriterFailure = S_OK;
// Get writer status
CHECK_COM(m_pVssObject->GetWriterStatus(iWriter,
&idInstance,
&idWriter,
&bstrWriterName,
&eWriterStatus,
&hrWriterFailure));
// If the writer is not selected, just continue
if (!IsWriterSelected(idInstance))
continue;
// If the writer is in non-stable state, break
switch(eWriterStatus)
{
case VSS_WS_FAILED_AT_IDENTIFY:
case VSS_WS_FAILED_AT_PREPARE_BACKUP:
case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
case VSS_WS_FAILED_AT_FREEZE:
case VSS_WS_FAILED_AT_THAW:
case VSS_WS_FAILED_AT_POST_SNAPSHOT:
case VSS_WS_FAILED_AT_BACKUP_COMPLETE:
case VSS_WS_FAILED_AT_PRE_RESTORE:
case VSS_WS_FAILED_AT_POST_RESTORE:
#ifdef VSS_SERVER
case VSS_WS_FAILED_AT_BACKUPSHUTDOWN:
#endif
break;
default:
continue;
}
// Print writer status
ft.WriteLine(L"\n"
L"ERROR: Selected writer '%s' is in failed state!\n"
L" - Status: %d (%s)\n"
L" - Writer Failure code: 0x%08lx (%s)\n"
L" - Writer ID: " WSTR_GUID_FMT L"\n"
L" - Instance ID: " WSTR_GUID_FMT L"\n",
(PWCHAR)bstrWriterName,
eWriterStatus, GetStringFromWriterStatus(eWriterStatus).c_str(),
hrWriterFailure,FunctionTracer::HResult2String(hrWriterFailure).c_str(),
GUID_PRINTF_ARG(idWriter),
GUID_PRINTF_ARG(idInstance)
);
// Stop here
throw(E_UNEXPECTED);
}
}