#include "shadow.h" #include //includes "windows.h" #include #include "systemConstants.h" using FreeFileSync::ShadowCopy; class ShadowlDllHandler //dynamically load windows API functions { typedef bool (*CreateShadowCopyFct)( //volumeName must end with "\", while shadowVolName does not end with "\" const wchar_t* volumeName, wchar_t* shadowVolName, unsigned int shadowBufferLen, void** backupHandle, wchar_t* errorMessage, unsigned int errorBufferLen); typedef void (*ReleaseShadowCopyFct)(void* backupHandle); public: static const ShadowlDllHandler& getInstance() { static ShadowlDllHandler instance; return instance; } CreateShadowCopyFct createShadowCopy; ReleaseShadowCopyFct releaseShadowCopy; private: ShadowlDllHandler() : createShadowCopy(NULL), releaseShadowCopy(NULL), hShadow(NULL) { //get a handle to the DLL module containing the required functionality hShadow = ::LoadLibrary(L"Shadow.dll"); if (hShadow) { createShadowCopy = reinterpret_cast(::GetProcAddress(hShadow, "createShadowCopy")); releaseShadowCopy = reinterpret_cast(::GetProcAddress(hShadow, "releaseShadowCopy")); } } ~ShadowlDllHandler() { if (hShadow) ::FreeLibrary(hShadow); } HINSTANCE hShadow; }; ShadowCopy::ShadowCopy() : backupHandle(NULL) {} ShadowCopy::~ShadowCopy() { if (backupHandle != NULL) ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle); } bool ShadowCopy::isOkay() { //check that all functions could be loaded return ShadowlDllHandler::getInstance().createShadowCopy != NULL && ShadowlDllHandler::getInstance().releaseShadowCopy != NULL; } Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) { //check if shadow copy dll was loaded correctly if (!isOkay()) { wxString errorMsg = _("Error copying locked file %x!"); errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + inputFile.c_str() + wxT("\"")); throw FileError(errorMsg + wxT("\n\n") + _("Error starting Volume Shadow Copy Service!") + wxT("\n") + _("Please copy the appropriate \"Shadow.dll\" (located in \"Shadow.zip\" archive) into the FreeFileSync installation directory to enable this feature.")); } wchar_t volumeNameRaw[1000]; if (!GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName, volumeNameRaw, //__out LPTSTR lpszVolumePathName, 1000)) //__in DWORD cchBufferLength { wxString errorMsg = _("Error copying locked file %x!"); errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + inputFile.c_str() + wxT("\"")); throw FileError(errorMsg + wxT("\n\n") + _("Could not determine volume name for file:") + wxT("\n\"") + inputFile.c_str() + wxT("\"")); } Zstring volumeNameFormatted = volumeNameRaw; if (!volumeNameFormatted.EndsWith(globalFunctions::FILE_NAME_SEPARATOR)) volumeNameFormatted += globalFunctions::FILE_NAME_SEPARATOR; if (volumeNameFormatted != realVolumeLast) { //release old shadow copy if (backupHandle != NULL) { ShadowlDllHandler::getInstance().releaseShadowCopy(backupHandle); backupHandle = NULL; } realVolumeLast.clear(); //...if next call fails... shadowVolumeLast.clear(); //...if next call fails... //start shadow volume copy service: wchar_t shadowVolName[1000]; void* backupHandleTmp = NULL; wchar_t errorMessage[1000]; if (!ShadowlDllHandler::getInstance().createShadowCopy( volumeNameFormatted.c_str(), shadowVolName, 1000, &backupHandleTmp, errorMessage, 1000)) { wxString errorMsg = _("Error copying locked file %x!"); errorMsg.Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")); throw FileError(errorMsg + wxT("\n\n") + _("Error starting Volume Shadow Copy Service!") + wxT("\n") + wxT("(") + errorMessage + wxT(")")); } realVolumeLast = volumeNameFormatted; shadowVolumeLast = Zstring(shadowVolName) + globalFunctions::FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash backupHandle = backupHandleTmp; } const size_t pos = inputFile.find(volumeNameFormatted); if (pos == Zstring::npos) { wxString errorMsg = _("Error copying locked file %x!"); errorMsg.Replace(wxT("%x"), Zstring(wxT("\"")) + inputFile + wxT("\"")); wxString msg = _("Volume name %x not part of filename %y!"); msg.Replace(wxT("%x"), wxString(wxT("\"")) + volumeNameFormatted.c_str() + wxT("\""), false); msg.Replace(wxT("%y"), wxString(wxT("\"")) + inputFile.c_str() + wxT("\""), false); throw FileError(errorMsg + wxT("\n\n") + msg); } //return filename alias on shadow copy volume return shadowVolumeLast + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length()); }