// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved * // ************************************************************************** #include "shadow.h" #include #include #include #include #include #ifdef USE_SHADOW_XP #include "xp/inc/vss.h" #include "xp/inc/vswriter.h" #include "xp/inc/vsbackup.h" #elif defined USE_SHADOW_2003 #include "Server 2003/inc/vss.h" #include "Server 2003/inc/vswriter.h" #include "Server 2003/inc/vsbackup.h" #elif defined USE_SHADOW_WINDOWS7 #include // #include //part of Windows SDK for Windows 7 #include // #pragma comment(lib, "VssApi.lib") #else #error adapt! #endif using namespace zen; struct shadow::ShadowData { ShadowData(const ComPtr& backupComp, const std::wstring& shadowVolume) : backupComp_(backupComp), shadowVolume_(shadowVolume) {} ComPtr backupComp_; std::wstring shadowVolume_; }; namespace { std::wstring formatVssError(HRESULT hr) //at least the one's from IVssBackupComponents::AddToSnapshotSet; return empty if no format found { switch (hr) { case VSS_E_BAD_STATE: return L"VSS_E_BAD_STATE"; case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: return L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: return L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; case VSS_E_OBJECT_NOT_FOUND: return L"VSS_E_OBJECT_NOT_FOUND"; case VSS_E_PROVIDER_NOT_REGISTERED: return L"VSS_E_PROVIDER_NOT_REGISTERED"; case VSS_E_PROVIDER_VETO: return L"VSS_E_PROVIDER_VETO"; case VSS_E_VOLUME_NOT_SUPPORTED: return L"VSS_E_VOLUME_NOT_SUPPORTED"; case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: return L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; case VSS_E_UNEXPECTED_PROVIDER_ERROR: return L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; default: return std::wstring(); } } inline void copyString(const std::wstring& input, wchar_t* buffer, size_t bufferSize) { if (bufferSize > 0) { //size_t endPos = input.copy(buffer, bufferSize - 1); //buffer[endPos] = 0; const size_t maxSize = std::min(input.length(), bufferSize - 1); std::copy(input.begin(), input.begin() + maxSize, buffer); buffer[maxSize] = 0; } } shadow::ShadowData createShadowCopy(const wchar_t* volumeName) //throw ComError { ComPtr backupComp; { HRESULT hr = ::CreateVssBackupComponents(backupComp.init()); if (FAILED(hr)) { if (hr == E_ACCESSDENIED) throw ComError(L"The caller does not have sufficient backup privileges or is not an administrator.", hr); throw ComError(L"Error calling \"CreateVssBackupComponents\".", hr); } } ZEN_CHECK_COM(backupComp->InitializeForBackup()); //throw ComError ZEN_CHECK_COM(backupComp->SetBackupState(false, false, VSS_BT_FULL)); //throw ComError auto waitForComFuture = [](IVssAsync& fut) { ZEN_CHECK_COM(fut.Wait()); HRESULT hr = S_OK; ZEN_CHECK_COM(fut.QueryStatus(&hr, NULL)); //check if the async operation succeeded... if (FAILED(hr)) throw ComError(L"Error calling \"fut->QueryStatus\".", hr); }; ComPtr gatherAsync; ZEN_CHECK_COM(backupComp->GatherWriterMetadata(gatherAsync.init())); waitForComFuture(*gatherAsync); //failure can happen if XP-version of VSS is used on Windows Vista (which needs at least VSS-Server2003 build) VSS_ID snapshotSetId = {}; ZEN_CHECK_COM(backupComp->StartSnapshotSet(&snapshotSetId)); ScopeGuard guardSnapShot = makeGuard([&] { backupComp->AbortBackup(); }); //Quote: "This method must be called if a backup operation terminates after the creation of a //shadow copy set with "StartSnapshotSet" and before "DoSnapshotSet" returns." VSS_ID SnapShotId = {}; { HRESULT hr = backupComp->AddToSnapshotSet(const_cast(volumeName), GUID_NULL, &SnapShotId); if (FAILED(hr)) { if (hr == VSS_E_VOLUME_NOT_SUPPORTED) throw ComError(L"Volume Shadow Copy Service is not supported on this volume!"); const std::wstring vssError = formatVssError(hr); if (!vssError.empty()) throw ComError(L"Error calling \"backupComp->AddToSnapshotSet\": " + vssError); else throw ComError(L"Error calling \"backupComp->AddToSnapshotSet\".", hr); } } ComPtr prepareAsync; ZEN_CHECK_COM(backupComp->PrepareForBackup(prepareAsync.init())); waitForComFuture(*prepareAsync); ComPtr snapshotAsync; ZEN_CHECK_COM(backupComp->DoSnapshotSet(snapshotAsync.init())); guardSnapShot.dismiss(); waitForComFuture(*snapshotAsync); VSS_SNAPSHOT_PROP props = {}; ZEN_CHECK_COM(backupComp->GetSnapshotProperties(SnapShotId, &props)); ZEN_ON_BLOCK_EXIT(::VssFreeSnapshotProperties(&props)); //finally: write volume name of newly created shadow copy return shadow::ShadowData(backupComp, props.m_pwszSnapshotDeviceObject); } } shadow::ShadowHandle shadow::createShadowCopy(const wchar_t* volumeName, wchar_t* errorMessage, unsigned int errorBufferLen) { try { ShadowData result = ::createShadowCopy(volumeName); //shadow handle owned by caller! throw ComError return new ShadowData(result); } catch (const zen::ComError& e) { copyString(e.toString(), errorMessage, errorBufferLen); return nullptr; } } void shadow::getShadowVolume(shadow::ShadowHandle handle, wchar_t* buffer, unsigned int bufferLen) { copyString(handle->shadowVolume_, buffer, bufferLen); } void shadow::releaseShadowCopy(ShadowHandle handle) { delete handle; }