void VssClient::BreakSnapshotSetEx(VSS_ID snapshotSetID, DWORD dwBreakExFlags) { FunctionTracer ft(DBG_INFO); ft.WriteLine(L"- Calling BreakSnapshotSetEx on " WSTR_GUID_FMT L" ...", GUID_PRINTF_ARG(snapshotSetID)); if (!(dwBreakExFlags & VSS_BREAKEX_FLAG_MASK_LUNS) && !(dwBreakExFlags & VSS_BREAKEX_FLAG_MAKE_READ_WRITE) && !(dwBreakExFlags & VSS_BREAKEX_FLAG_REVERT_IDENTITY_ALL) && !(dwBreakExFlags & VSS_BREAKEX_FLAG_REVERT_IDENTITY_NONE)) ft.WriteLine(L"- No BreakSnapshotSetEx flags, can use -mask, -rw, -forcerevert, or -norevert"); CComPtr<IVssBackupComponentsEx2> pVssObjectEx; CHECK_COM(m_pVssObject->QueryInterface(__uuidof(IVssBackupComponentsEx2), (void**)&pVssObjectEx)); CComPtr<IVssAsync> pAsync; CHECK_COM(pVssObjectEx->BreakSnapshotSetEx(snapshotSetID, dwBreakExFlags, &pAsync)); // Waits for the async operation to finish and checks the result WaitAndCheckForAsyncOperation(pAsync); ft.WriteLine(L"BreakEx done."); }
// Lists the status for all writers void VssClient::ListWriterStatus() { FunctionTracer ft(DBG_INFO); ft.WriteLine(L"Listing writer status ..."); // 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)); ft.WriteLine(L"- Number of writers that responded: %u", 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)); // Print writer status ft.WriteLine(L"\n" L"* WRITER \"%s\"\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) ); } }
// // Break the given shadow copy set to read or read-write // // Setting the parameter pVolumeNames makes this function return immediately after VSS breaks the // shadow copy set. This is useful in the fast recover scenario: if the LUN after break is in an // offline mode, the requster must wait for the LUN online in order to call VDS to make it read-write. void VssClient::BreakSnapshotSet(VSS_ID snapshotSetID, bool makeReadWrite, vector<wstring> *pVolumeNames) { FunctionTracer ft(DBG_INFO); if (makeReadWrite) { // If we want read-write treatment, compute a list of volumes in the shadow copy set vector<wstring> snapshotDeviceList; snapshotDeviceList = GetSnapshotDevices(snapshotSetID); ft.WriteLine(L"- Calling BreakSnapshotSet on " WSTR_GUID_FMT L" ...", GUID_PRINTF_ARG(snapshotSetID)); // Break the shadow copy set CHECK_COM(m_pVssObject->BreakSnapshotSet(snapshotSetID)); // If we want to delay read-write treatment, fill out the volume name list and return if (pVolumeNames) { *pVolumeNames = snapshotDeviceList; return; } ft.WriteLine(L"- Making shadow copy devices from " WSTR_GUID_FMT L" read-write...", GUID_PRINTF_ARG(snapshotSetID)); // Make the snapshot devices read-write MakeVolumesReadWrite(snapshotDeviceList); } else { ft.WriteLine(L"- Calling BreakSnapshotSet on " WSTR_GUID_FMT L" ...", GUID_PRINTF_ARG(snapshotSetID)); // Just break the snapshot set CHECK_COM(m_pVssObject->BreakSnapshotSet(snapshotSetID)); ft.WriteLine(L"Break done."); } }
// Delete the given shadow copy set void VssClient::DeleteSnapshotSet(VSS_ID snapshotSetID) { FunctionTracer ft(DBG_INFO); // Print the deleted shadow copy... ft.WriteLine(L"- Deleting shadow copy set " WSTR_GUID_FMT L" ...", GUID_PRINTF_ARG(snapshotSetID)); // Perform the actual deletion LONG lSnapshots = 0; VSS_ID idNonDeletedSnapshotID = GUID_NULL; HRESULT hr = m_pVssObject->DeleteSnapshots( snapshotSetID, VSS_OBJECT_SNAPSHOT_SET, FALSE, &lSnapshots, &idNonDeletedSnapshotID); if (FAILED(hr)) { ft.WriteLine(L"Error while deleting shadow copies..."); ft.WriteLine(L"- Last shadow copy that could not be deleted: " WSTR_GUID_FMT, GUID_PRINTF_ARG(idNonDeletedSnapshotID)); CHECK_COM_ERROR(hr, L"m_pVssObject->DeleteSnapshots(snapshotSetID, VSS_OBJECT_SNAPSHOT_SET,FALSE,&lSnapshots,&idNonDeleted)"); } }
// Print the properties for the given snasphot void VssClient::PrintSnapshotProperties(VSS_SNAPSHOT_PROP & prop) { FunctionTracer ft(DBG_INFO); LONG lAttributes = prop.m_lSnapshotAttributes; ft.WriteLine(L"* SNAPSHOT ID = " WSTR_GUID_FMT L" ...", GUID_PRINTF_ARG(prop.m_SnapshotId)); ft.WriteLine(L" - Shadow copy Set: " WSTR_GUID_FMT, GUID_PRINTF_ARG(prop.m_SnapshotSetId)); ft.WriteLine(L" - Original count of shadow copies = %d", prop.m_lSnapshotsCount); ft.WriteLine(L" - Original Volume name: %s [%s]", prop.m_pwszOriginalVolumeName, GetDisplayNameForVolume(prop.m_pwszOriginalVolumeName).c_str() ); ft.WriteLine(L" - Creation Time: %s", VssTimeToString(prop.m_tsCreationTimestamp).c_str()); ft.WriteLine(L" - Shadow copy device name: %s", prop.m_pwszSnapshotDeviceObject); ft.WriteLine(L" - Originating machine: %s", prop.m_pwszOriginatingMachine); ft.WriteLine(L" - Service machine: %s", prop.m_pwszServiceMachine); if (prop.m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY) ft.WriteLine(L" - Exposed locally as: %s", prop.m_pwszExposedName); else if (prop.m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY) { ft.WriteLine(L" - Exposed remotely as %s", prop.m_pwszExposedName); if (prop.m_pwszExposedPath && wcslen(prop.m_pwszExposedPath) > 0) ft.WriteLine(L" - Path exposed: %s", prop.m_pwszExposedPath); } else ft.WriteLine(L" - Not Exposed"); ft.WriteLine(L" - Provider id: " WSTR_GUID_FMT, GUID_PRINTF_ARG(prop.m_ProviderId)); // Display the attributes wstring attributes; if (lAttributes & VSS_VOLSNAP_ATTR_TRANSPORTABLE) attributes += wstring(L" Transportable"); if (lAttributes & VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE) attributes += wstring(L" No_Auto_Release"); else attributes += wstring(L" Auto_Release"); if (lAttributes & VSS_VOLSNAP_ATTR_PERSISTENT) attributes += wstring(L" Persistent"); if (lAttributes & VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE) attributes += wstring(L" Client_accessible"); if (lAttributes & VSS_VOLSNAP_ATTR_HARDWARE_ASSISTED) attributes += wstring(L" Hardware"); if (lAttributes & VSS_VOLSNAP_ATTR_NO_WRITERS) attributes += wstring(L" No_Writers"); if (lAttributes & VSS_VOLSNAP_ATTR_IMPORTED) attributes += wstring(L" Imported"); if (lAttributes & VSS_VOLSNAP_ATTR_PLEX) attributes += wstring(L" Plex"); if (lAttributes & VSS_VOLSNAP_ATTR_DIFFERENTIAL) attributes += wstring(L" Differential"); ft.WriteLine(L" - Attributes: %s", attributes.c_str()); ft.WriteLine(L""); }
// Query all the shadow copies in the given set // If snapshotSetID is NULL, just query all shadow copies in the system void VssClient::QuerySnapshotSet(VSS_ID snapshotSetID) { FunctionTracer ft(DBG_INFO); if (snapshotSetID == GUID_NULL) ft.WriteLine(L"\nQuerying all shadow copies in the system ...\n"); else ft.WriteLine(L"\nQuerying all shadow copies with the SnapshotSetID " WSTR_GUID_FMT L" ...\n", GUID_PRINTF_ARG(snapshotSetID)); // Get list all shadow copies. CComPtr<IVssEnumObject> pIEnumSnapshots; HRESULT hr = m_pVssObject->Query( GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_SNAPSHOT, &pIEnumSnapshots ); CHECK_COM_ERROR(hr, L"m_pVssObject->Query(GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_SNAPSHOT, &pIEnumSnapshots )") // If there are no shadow copies, just return if (hr == S_FALSE) { if (snapshotSetID == GUID_NULL) ft.WriteLine(L"\nThere are no shadow copies in the system\n"); return; } // Enumerate all shadow copies. VSS_OBJECT_PROP Prop; VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap; while(true) { // Get the next element ULONG ulFetched; hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched ); CHECK_COM_ERROR(hr, L"pIEnumSnapshots->Next( 1, &Prop, &ulFetched )") // We reached the end of list if (ulFetched == 0) break; // Automatically call VssFreeSnapshotProperties on this structure at the end of scope CAutoSnapPointer snapAutoCleanup(&Snap); // Print the shadow copy (if not filtered out) if ((snapshotSetID == GUID_NULL) || (Snap.m_SnapshotSetId == snapshotSetID)) PrintSnapshotProperties(Snap); } }
// Check the status for all selected writers. HRESULT GoogleVssClient::CheckSelectedWriterStatus() { // Gather writer status to detect potential errors. HRESULT hr = GatherWriterStatus(); if (SUCCEEDED(hr)) { // Gets the number of writers in the gathered status info // (WARNING: GatherWriterStatus must be called before). unsigned writers = 0; hr = vss_object_->GetWriterStatusCount(&writers); if (FAILED(hr)) { LogDebugMessage(L"GetWriterStatusCount failed with error %x ", hr); } else { // Enumerate each writer. HRESULT hrWriterFailure = S_OK; for (unsigned writer = 0; writer < writers; writer++) { VSS_ID idInstance = GUID_NULL; VSS_ID idWriter = GUID_NULL; VSS_WRITER_STATE writerStatus = VSS_WS_UNKNOWN; CComBSTR bstrWriterName; // Get writer status. hr = vss_object_->GetWriterStatus(writer, &idInstance, &idWriter, &bstrWriterName, &writerStatus, &hrWriterFailure); // If the writer is not selected, just continue. if (!IsWriterSelected(idInstance)) { continue; } // If the writer is in non-stable state, break. switch (writerStatus) { 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: // Print writer status. LogDebugMessage( L"ERROR: Selected writer '%s' is in failed state." L" Status: %d (%s), Writer Failure code: 0x%08lx," L" Writer ID: " WSTR_GUID_FMT L" Instance ID: " WSTR_GUID_FMT, BstrToWString(bstrWriterName).c_str(), writerStatus, GetStringFromWriterStatus(writerStatus).c_str(), hrWriterFailure, GUID_PRINTF_ARG(idWriter), GUID_PRINTF_ARG(idInstance)); // Stop here. hr = E_UNEXPECTED; break; default: break; } if (FAILED(hr)) { break; } } } } LogDebugMessage(L"CheckSelectedWriterStatus returned with %x", hr); return hr; }
void VssClient::RevertToSnapshot(VSS_ID snapshotID) { FunctionTracer ft(DBG_INFO); // Get the shadow copy properties VSS_SNAPSHOT_PROP Snap; CHECK_COM(m_pVssObject->GetSnapshotProperties(snapshotID, &Snap)); // Automatically call VssFreeSnapshotProperties on this structure at the end of scope CAutoSnapPointer snapAutoCleanup(&Snap); ft.WriteLine(L"- Reverting to shadow copy " WSTR_GUID_FMT L" on %s from provider " WSTR_GUID_FMT L" [0x%08lx]...", GUID_PRINTF_ARG(Snap.m_SnapshotId), Snap.m_pwszOriginalVolumeName, GUID_PRINTF_ARG(Snap.m_ProviderId), Snap.m_lSnapshotAttributes); bool bBlock = false; CHECK_COM(::ShouldBlockRevert(Snap.m_pwszOriginalVolumeName, &bBlock)); if (bBlock) { ft.WriteLine(L"Revert is disabled on the volume %s because of writers", Snap.m_pwszOriginalVolumeName); return; } HRESULT hr = m_pVssObject->RevertToSnapshot(snapshotID, true); if (FAILED(hr)) { switch (hr) { case VSS_E_OBJECT_NOT_FOUND: ft.WriteLine(L"Shadow Copy with id " WSTR_GUID_FMT L" was not found", GUID_PRINTF_ARG(snapshotID)); return; case VSS_E_VOLUME_IN_USE: ft.WriteLine(L"The voulume %s cannot be reverted since it is in use", Snap.m_pwszOriginalVolumeName); return; case VSS_E_REVERT_IN_PROGRESS: ft.WriteLine(L"A revert is current in progress on the volume %s", Snap.m_pwszOriginalVolumeName); return; case VSS_E_VOLUME_NOT_SUPPORTED: ft.WriteLine(L"Revert is not supported on the volume %s", Snap.m_pwszOriginalVolumeName); return; default: ft.WriteLine(L"RevertToSnapshot on Shadow Copy " WSTR_GUID_FMT L" failed with error 0x%08lx", GUID_PRINTF_ARG(snapshotID), hr); return; } } CComPtr<IVssAsync> pAsync; hr = m_pVssObject->QueryRevertStatus(Snap.m_pwszOriginalVolumeName, &pAsync); if (hr != VSS_E_OBJECT_NOT_FOUND) { if (FAILED(hr)) { ft.WriteLine(L"QueryRevertStatus failed with error 0x%08lx", hr); ft.WriteLine(L"Revert may still be in progress, but cannot be tracked"); return; } hr = pAsync->Wait(); if (FAILED(hr)) { ft.WriteLine(L"IVssAsync::Wait failed with error 0x%08lx", hr); ft.WriteLine(L"Revert may still be in progress, but cannot be tracked"); return; } } ft.WriteLine(L"The shadow copy has been successfully reverted"); }
// Delete all the shadow copies in the system void VssClient::DeleteAllSnapshots() { FunctionTracer ft(DBG_INFO); // Get list all shadow copies. CComPtr<IVssEnumObject> pIEnumSnapshots; HRESULT hr = m_pVssObject->Query( GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_SNAPSHOT, &pIEnumSnapshots ); CHECK_COM_ERROR(hr, L"m_pVssObject->Query(GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_SNAPSHOT, &pIEnumSnapshots )") // If there are no shadow copies, just return if (hr == S_FALSE) { ft.WriteLine(L"\nThere are no shadow copies on the system\n"); return; } // Enumerate all shadow copies. Delete each one VSS_OBJECT_PROP Prop; VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap; while(true) { // Get the next element ULONG ulFetched; hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched ); CHECK_COM_ERROR(hr, L"pIEnumSnapshots->Next( 1, &Prop, &ulFetched )") // We reached the end of list if (ulFetched == 0) break; // Automatically call VssFreeSnapshotProperties on this structure at the end of scope CAutoSnapPointer snapAutoCleanup(&Snap); // Print the deleted shadow copy... ft.WriteLine(L"- Deleting shadow copy " WSTR_GUID_FMT L" on %s from provider " WSTR_GUID_FMT L" [0x%08lx]...", GUID_PRINTF_ARG(Snap.m_SnapshotId), Snap.m_pwszOriginalVolumeName, GUID_PRINTF_ARG(Snap.m_ProviderId), Snap.m_lSnapshotAttributes); // Perform the actual deletion LONG lSnapshots = 0; VSS_ID idNonDeletedSnapshotID = GUID_NULL; hr = m_pVssObject->DeleteSnapshots( Snap.m_SnapshotId, VSS_OBJECT_SNAPSHOT, FALSE, &lSnapshots, &idNonDeletedSnapshotID); if (FAILED(hr)) { ft.WriteLine(L"Error while deleting shadow copies..."); ft.WriteLine(L"- Last shadow copy that could not be deleted: " WSTR_GUID_FMT, GUID_PRINTF_ARG(idNonDeletedSnapshotID)); CHECK_COM_ERROR(hr, L"m_pVssObject->DeleteSnapshots(Snap.m_SnapshotId, VSS_OBJECT_SNAPSHOT,FALSE,&lSnapshots,&idNonDeleted)"); } } }