///////////////////////////////////////////////////////////////////////// // 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 shadowSourceVolumes, vector excludedWriterAndComponentList, vector 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 excludedWriterAndComponentList, vector 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 excludedWriterAndComponentList, vector & 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 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 & 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 & 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); } }