summaryrefslogtreecommitdiff
path: root/shared/shadow.cpp
blob: 7e3b34c0a2422425f6b642ec31285aff8d939557 (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
#include "shadow.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/intl.h>
#include "systemConstants.h"
#include "dllLoader.h"
#include <stdexcept>
#include "staticAssert.h"
#include "buildInfo.h"

using FreeFileSync::ShadowCopy;


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

    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;
}


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
    */

    static const wxString filename(
        Utility::is64BitBuild ?
        (newerThanXP() ?
         wxT("Shadow_Server2003_x64.dll") :
         wxT("Shadow_XP_x64.dll")) :

            (newerThanXP() ?
             wxT("Shadow_Server2003_Win32.dll") :
             wxT("Shadow_XP_Win32.dll")));

    assert_static(Utility::is32BitBuild || Utility::is64BitBuild);

    return filename;
}



//#############################################################################################################
ShadowCopy::ShadowCopy() :
    backupHandle(NULL) {}


ShadowCopy::~ShadowCopy()
{
    if (backupHandle != NULL)
    {
        typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
        static const ReleaseShadowCopyFct releaseShadowCopy =
            Utility::loadDllFunction<ReleaseShadowCopyFct>(getShadowDllName().c_str(), "releaseShadowCopy");

        if (releaseShadowCopy == NULL)
            throw std::logic_error("Could not load \"releaseShadowCopy\"!"); //shouldn't arrive here!

        releaseShadowCopy(backupHandle);
    }
}


Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
{
    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);
    static const CreateShadowCopyFct createShadowCopy =
        Utility::loadDllFunction<CreateShadowCopyFct>(getShadowDllName().c_str(), "createShadowCopy");


    typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
    static const ReleaseShadowCopyFct releaseShadowCopy =
        Utility::loadDllFunction<ReleaseShadowCopyFct>(getShadowDllName().c_str(), "releaseShadowCopy");



    //check if shadow copy dll was loaded correctly
    if (    createShadowCopy  == NULL ||
            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(" \"") + 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)
        {
            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 (!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;
    }

    //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)
    {
        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