// ************************************************************************** // * 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 (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** #include "shadow.h" #include #include //includes "windows.h" #include #include #include #include #include #include "ShadowCopy/shadow.h" #include using namespace zen; using namespace shadow; namespace { bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx) { typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); const SysDllFun isWow64Process(L"kernel32.dll", "IsWow64Process"); if (isWow64Process) { BOOL isWow64 = FALSE; if (isWow64Process(::GetCurrentProcess(), &isWow64)) return isWow64 != FALSE; } return false; } const bool wereVistaOrLater = vistaOrLater(); //thread-safety: init at startup } //############################################################################################################# class ShadowCopy::ShadowVolume { public: ShadowVolume(const Zstring& volumeNamePf) : //throw FileError createShadowCopy (getDllName(), funName_createShadowCopy), releaseShadowCopy(getDllName(), funName_releaseShadowCopy), getShadowVolume (getDllName(), funName_getShadowVolume), getLastError (getDllName(), funName_getLastError), backupHandle(nullptr) { //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 //reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx if (runningWOW64()) throw FileError(_("Cannot access Volume Shadow Copy Service."), _("Please use FreeFileSync 64-bit version to create shadow copies on this system.")); //check if shadow copy dll was loaded correctly if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastError) throw FileError(_("Cannot access Volume Shadow Copy Service."), replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); //--------------------------------------------------------------------------------------------------------- //start volume shadow copy service: backupHandle = createShadowCopy(volumeNamePf.c_str()); if (!backupHandle) throw FileError(_("Cannot access Volume Shadow Copy Service."), getLastError() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf)); shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash } ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator private: ShadowVolume(const ShadowVolume&); ShadowVolume& operator=(const ShadowVolume&); const DllFun createShadowCopy; const DllFun releaseShadowCopy; const DllFun getShadowVolume; const DllFun getLastError; Zstring shadowVolPf; ShadowHandle backupHandle; }; //############################################################################################################# Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy) { Zstring filenameFinal = inputFile; //try to resolve symlinks and junctions: //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked! //2. junctions: C:\Users\ is a junction that may link to e.g. D:\Users\, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y." if (wereVistaOrLater) filenameFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later! //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix DWORD bufferSize = 10000; std::vector volBuffer(bufferSize); if (!::GetVolumePathName(filenameFinal.c_str(), //__in LPCTSTR lpszFileName, &volBuffer[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength throw FileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filenameFinal)), formatSystemError(L"GetVolumePathName", ::GetLastError())); const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error! //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found. const size_t pos = filenameFinal.find(volumeNamePf); //filenameFinal needs NOT to begin with volumeNamePf: consider for example \\?\ prefix! if (pos == Zstring::npos) { std::wstring msg = _("Volume name %x not part of file name %y."); replace(msg, L"%x", fmtFileName(volumeNamePf), false); replace(msg, L"%y", fmtFileName(filenameFinal), false); throw FileError(msg); } //get or create instance of shadow volume VolNameShadowMap::const_iterator it = shadowVol.find(volumeNamePf); if (it == shadowVol.end()) { onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time auto newEntry = std::make_shared(volumeNamePf); //throw FileError it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; } //return filename alias on shadow copy volume return it->second->geNamePf() + Zstring(filenameFinal.c_str() + pos + volumeNamePf.length()); }