summaryrefslogtreecommitdiff
path: root/lib/shadow.cpp
blob: 0a34ae5a49dbff490c01a74dba84d93e5bfb170f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// **************************************************************************
// * 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 <stdexcept>
#include <zen/win.h> //includes "windows.h"
#include <zen/dll.h>
#include <zen/assert_static.h>
#include "ShadowCopy/shadow.h"
#include <zen/scope_guard.h>

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<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process");
    if (isWow64Process)
    {
        BOOL isWow64 = FALSE;
        if (isWow64Process(::GetCurrentProcess(), &isWow64))
            return isWow64 == TRUE;
    }

    return false;
}
}

//#############################################################################################################


class ShadowCopy::ShadowVolume
{
public:
    ShadowVolume(const Zstring& volumeNameFormatted) : //throw(FileError)
        createShadowCopy (getDllName(), createShadowCopyFctName),
        releaseShadowCopy(getDllName(), releaseShadowCopyFctName),
        getShadowVolume  (getDllName(), getShadowVolumeFctName),
        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(_("Error accessing Volume Shadow Copy Service!") + L"\n" +
                            _("Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version."));


        //check if shadow copy dll was loaded correctly
        if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume)
            throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" +
                            replaceCpy(_("Cannot load file %x."), L"%x", std::wstring(L"\"") + getDllName() + L"\""));

        //---------------------------------------------------------------------------------------------------------
        //start shadow volume copy service:
        const unsigned int BUFFER_SIZE = 10000;
        std::vector<wchar_t> msgBuffer(BUFFER_SIZE);
        backupHandle = createShadowCopy(volumeNameFormatted.c_str(),
                                        &msgBuffer[0], BUFFER_SIZE);
        if (!backupHandle)
            throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" +
                            &msgBuffer[0] + L" Volume: \"" + volumeNameFormatted + L"\"");

        std::vector<wchar_t> volBuffer(BUFFER_SIZE);
        getShadowVolume(backupHandle, &volBuffer[0], BUFFER_SIZE);
        shadowVol = Zstring(&volBuffer[0]) + FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash
    }

    ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary

    Zstring getShadowVolumeName() const { return shadowVol; } //with trailing path separator

private:
    ShadowVolume(const ShadowVolume&);
    ShadowVolume& operator=(const ShadowVolume&);

    const DllFun<CreateShadowCopyFct>  createShadowCopy;
    const DllFun<ReleaseShadowCopyFct> releaseShadowCopy;
    const DllFun<GetShadowVolumeFct>   getShadowVolume;

    Zstring shadowVol;

    ShadowHandle backupHandle;
};
//#############################################################################################################


Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
{
    std::vector<wchar_t> volBuffer(1000);
    if (!::GetVolumePathName(inputFile.c_str(), //__in   LPCTSTR lpszFileName,
                             &volBuffer[0],     //__out  LPTSTR lpszVolumePathName,
                             static_cast<DWORD>(volBuffer.size()))) //__in   DWORD cchBufferLength
        throw FileError(_("Cannot determine volume name for file:") + L"\n\"" + inputFile + L"\"");

    Zstring volumeNameFormatted = &volBuffer[0];
    if (!endsWith(volumeNameFormatted, FILE_NAME_SEPARATOR))
        volumeNameFormatted += FILE_NAME_SEPARATOR;

    //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
    const size_t pos = inputFile.find(volumeNameFormatted); //inputFile needs NOT to begin with volumeNameFormatted: consider for example \\?\ prefix!
    if (pos == Zstring::npos)
    {
        std::wstring msg = _("Volume name %x not part of file name %y!");
        replace(msg, L"%x", std::wstring(L"\"") + volumeNameFormatted + L"\"", false);
        replace(msg, L"%y", std::wstring(L"\"") + inputFile + L"\"", false);
        throw FileError(msg);
    }

    //get or create instance of shadow volume
    VolNameShadowMap::const_iterator iter = shadowVol.find(volumeNameFormatted);
    if (iter == shadowVol.end())
    {
        auto newEntry = std::make_shared<ShadowVolume>(volumeNameFormatted);
        iter = shadowVol.insert(std::make_pair(volumeNameFormatted, newEntry)).first;
    }

    //return filename alias on shadow copy volume
    return iter->second->getShadowVolumeName() + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
}
bgstack15