summaryrefslogtreecommitdiff
path: root/shared/shadow.cpp
blob: 29eec53b5f32e183b8769143344a7b5c821102aa (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#include "shadow.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/intl.h>
#include "systemConstants.h"

using FreeFileSync::ShadowCopy;


bool newerThanXP()
{
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    //symbolic links are supported starting with Vista
    if (GetVersionEx(&osvi))
        return osvi.dwMajorVersion > 5 ||
               (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion > 1) ;
    //XP          has majorVersion == 5, minorVersion == 1
    //Server 2003 has majorVersion == 5, minorVersion == 2
    //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
    return false;
}


bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx)
{
    typedef BOOL (WINAPI *IsWow64ProcessFunc)(
        HANDLE hProcess,
        PBOOL Wow64Process);

    const IsWow64ProcessFunc isWow64Process = reinterpret_cast<IsWow64ProcessFunc>(
                ::GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"));
    if (isWow64Process != NULL)
    {
        BOOL isWow64 = FALSE;
        if ((*isWow64Process)(::GetCurrentProcess(), &isWow64))
            return isWow64 == TRUE;
    }

    return false;
}


class ShadowCopy::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 wxString& getShadowDllName()
    {
        /*
        distinguish a bunch of VSS builds: we use XP and Server 2003 implementations...
        VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx
        */
#if defined _WIN64 //note: _WIN32 is defined for 64-bit compilations, too, while _WIN64 only for 64-bit
        static const wxString filename(newerThanXP() ?
                                       wxT("Shadow_Server2003_x64.dll") :
                                       wxT("Shadow_XP_x64.dll"));
#elif defined(_WIN32)
        static const wxString filename(newerThanXP() ?
                                       wxT("Shadow_Server2003_win32.dll") :
                                       wxT("Shadow_XP_win32.dll"));
#else
        Are we at 128 bit already?
#endif
        return filename;
    }

    ShadowlDllHandler() :
        createShadowCopy(NULL),
        releaseShadowCopy(NULL),
        hShadow(NULL)
    {
        //get a handle to the DLL module containing the required functionality
        hShadow = ::LoadLibrary(getShadowDllName().c_str());
        if (hShadow)
        {
            createShadowCopy  = reinterpret_cast<CreateShadowCopyFct>(::GetProcAddress(hShadow, "createShadowCopy"));
            releaseShadowCopy = reinterpret_cast<ReleaseShadowCopyFct>(::GetProcAddress(hShadow, "releaseShadowCopy"));
        }
    }

    ~ShadowlDllHandler()
    {
        if (hShadow) ::FreeLibrary(hShadow);
    }

    CreateShadowCopyFct  createShadowCopy;
    ReleaseShadowCopyFct releaseShadowCopy;

private:
    HINSTANCE hShadow;
};


//#############################################################################################################
ShadowCopy::ShadowCopy() :
    backupHandle(NULL)
{
    shadowDll = new ShadowlDllHandler;
}


ShadowCopy::~ShadowCopy()
{
    if (backupHandle != NULL)
        shadowDll->releaseShadowCopy(backupHandle);

    delete shadowDll;
}


Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
{
    //check if shadow copy dll was loaded correctly
    if (    shadowDll->createShadowCopy  == NULL ||
            shadowDll->releaseShadowCopy == NULL)
    {
        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") +
                        _("Could not load a required DLL:") + wxT(" \"") + ShadowlDllHandler::getShadowDllName() + wxT("\""));
    }

    //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)
    static const bool wow64Active = runningWOW64();
    if (wow64Active)
    {
        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") +
                        _("Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version."));
    }


//---------------------------------------------------------------------------------------------------------
    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)
        {
            shadowDll->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 (!shadowDll->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());
}


bgstack15