summaryrefslogtreecommitdiff
path: root/shared/shadow.cpp
blob: da7cfd990670c8117dfd091846d3d583f624dec9 (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
#include "shadow.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/intl.h>
#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<CreateShadowCopyFct>(::GetProcAddress(hShadow, "createShadowCopy"));
            releaseShadowCopy = reinterpret_cast<ReleaseShadowCopyFct>(::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());
}









bgstack15