summaryrefslogtreecommitdiff
path: root/lib/shadow.cpp
blob: d00aa2f3332dd6549f1bdcc4cf8bc5ac9cf27b0f (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
// **************************************************************************
// * 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)
{
    //dynamically load windows API function
    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)
    {
        //check if shadow copy dll was loaded correctly
        if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume)
            throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" +
                            _("Could not load a required DLL:") + L" \"" + getDllName() + L"\"");

        //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."));

        //---------------------------------------------------------------------------------------------------------
        //start shadow volume copy service:
        wchar_t errorMessage[1000];
        backupHandle = createShadowCopy(volumeNameFormatted.c_str(),
                                        errorMessage,
                                        1000);
        if (!backupHandle)
            throw FileError(_("Error accessing Volume Shadow Copy Service!") + L"\n" +
                            L"(" +  errorMessage + L" Volume: \"" + volumeNameFormatted + L"\")");

        wchar_t shadowVolName[1000];
        getShadowVolume(backupHandle, shadowVolName, 1000);
        shadowVol = Zstring(shadowVolName) + FILE_NAME_SEPARATOR; //shadowVolName NEVER has a trailing backslash
    }

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

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

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)
{
    wchar_t volumeNameRaw[1000];

    if (!::GetVolumePathName(inputFile.c_str(), //__in   LPCTSTR lpszFileName,
                             volumeNameRaw,     //__out  LPTSTR lpszVolumePathName,
                             1000))             //__in   DWORD cchBufferLength
        throw FileError(_("Could not determine volume name for file:") + L"\n\"" + inputFile + L"\"");

    Zstring volumeNameFormatted = volumeNameRaw;
    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 filename %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())
    {
        std::shared_ptr<ShadowVolume> newEntry(new 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