]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/compat/vss.cpp
experimental vss support (needs some microsoft libs and include files + WIN32_VSS...
[bacula/bacula] / bacula / src / win32 / compat / vss.cpp
1 //                              -*- Mode: C++ -*-
2 // vss.cpp -- Interface to Volume Shadow Copies (VSS)
3 //
4 // Copyright transferred from MATRIX-Computer GmbH to
5 //   Kern Sibbald by express permission.
6 //
7 // Copyright (C) 2004-2005 Kern Sibbald
8 //
9 //   This program is free software; you can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License as
11 //   published by the Free Software Foundation; either version 2 of
12 //   the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the hope that it will be useful,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 //   General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public
20 //   License along with this program; if not, write to the Free
21 //   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 //   MA 02111-1307, USA.
23 //
24 // Author          : Thorsten Engel
25 // Created On      : Fri May 06 21:44:00 2006
26
27
28 #include <stdio.h>
29 #include <basetsd.h>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <process.h>
33 #include <direct.h>
34 #include <winsock2.h>
35 #include <windows.h>
36 #include <wincon.h>
37 #include <winbase.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <conio.h>
42 #include <process.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <time.h>
46 #include <signal.h>
47 #include <malloc.h>
48 #include <setjmp.h>
49 #include <direct.h>
50 #include <ctype.h>
51 #include <fcntl.h>
52 #include <io.h>
53
54
55 // STL includes
56 #include <vector>
57 #include <algorithm>
58 #include <string>
59 #include <fstream>
60 using namespace std;   
61
62 #include <atlcomcli.h>
63 #include <objbase.h>
64
65
66 // Used for safe string manipulation
67 #include <strsafe.h>
68
69 #include "vss/inc/WinXP/vss.h"
70 #include "vss/inc/WinXP/vswriter.h"
71 #include "vss/inc/WinXP/vsbackup.h"
72
73 #include "vss.h"
74
75 #pragma comment(lib,"C:/Development/bacula/bacula/src/win32/compat/vss/lib/WinXP/obj/i386/vssapi.lib")
76 #pragma comment(lib,"C:/Development/bacula/bacula/src/win32/compat/vss/lib/WinXP/obj/i386/vss_uuid.lib")
77 #pragma comment(lib,"atlsd.lib")
78
79
80
81 // define global VssClient
82 VSSClient g_VSSClient;
83
84 /* 
85  *
86  * some helper functions 
87  *
88  *
89  */
90
91 // Append a backslash to the current string 
92 inline wstring AppendBackslash(wstring str)
93 {
94     if (str.length() == 0)
95         return wstring(L"\\");
96     if (str[str.length() - 1] == L'\\')
97         return str;
98     return str.append(L"\\");
99 }
100
101 // Get the unique volume name for the given path
102 inline wstring GetUniqueVolumeNameForPath(wstring path)
103 {
104     _ASSERTE(path.length() > 0);
105
106     // Add the backslash termination, if needed
107     path = AppendBackslash(path);
108
109     // Get the root path of the volume
110     WCHAR volumeRootPath[MAX_PATH];
111     WCHAR volumeName[MAX_PATH];
112     WCHAR volumeUniqueName[MAX_PATH];
113
114     if (!GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
115       return L'\0';
116     
117     // Get the volume name alias (might be different from the unique volume name in rare cases)
118     if (!GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
119        return L'\0';
120     
121     // Get the unique volume name    
122     if (!GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
123        return L'\0';
124     
125     return volumeUniqueName;
126 }
127
128
129 // we need something like a map
130
131 // Constructor
132 VSSClient::VSSClient()
133 {
134     m_bCoInitializeCalled = false;
135     m_dwContext = VSS_CTX_BACKUP;
136     m_bDuringRestore = false;
137     m_bBackupIsInitialized = false;
138     m_pVssObject = NULL;
139     memset (m_wszUniqueVolumeName,0,sizeof (m_wszUniqueVolumeName));
140     memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
141 }
142
143 // Destructor
144 VSSClient::~VSSClient()
145 {
146    // Release the IVssBackupComponents interface 
147    // WARNING: this must be done BEFORE calling CoUninitialize()
148    if (m_pVssObject) {
149       m_pVssObject->Release();
150       m_pVssObject = NULL;
151    }
152
153    // Call CoUninitialize if the CoInitialize was performed sucesfully
154    if (m_bCoInitializeCalled)
155       CoUninitialize();
156 }
157
158 BOOL VSSClient::InitializeForBackup()
159 {
160     return Initialize (VSS_CTX_BACKUP);
161 }
162
163 // Initialize the COM infrastructure and the internal pointers
164 BOOL VSSClient::Initialize(DWORD dwContext, bool bDuringRestore)
165 {
166    HRESULT hr;
167    // Initialize COM 
168    if (!m_bCoInitializeCalled)  {
169       if (FAILED(CoInitialize(NULL)))
170          return FALSE;
171
172       m_bCoInitializeCalled = true;
173
174       // Initialize COM security
175       hr =
176          CoInitializeSecurity(
177          NULL,                           //  Allow *all* VSS writers to communicate back!
178          -1,                             //  Default COM authentication service
179          NULL,                           //  Default COM authorization service
180          NULL,                           //  reserved parameter
181          RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
182          RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities 
183          NULL,                           //  Default COM authentication settings
184          EOAC_NONE,                      //  No special options
185          NULL                            //  Reserved parameter
186          );
187
188       if (FAILED(hr))
189          return FALSE;
190    }
191
192    // Release the IVssBackupComponents interface 
193    if (m_pVssObject) {
194       m_pVssObject->Release();
195       m_pVssObject = NULL;
196    }
197
198    // Create the internal backup components object
199    hr = CreateVssBackupComponents(&m_pVssObject);
200    if (FAILED(hr))
201       return FALSE;
202
203    // We are during restore now?
204    m_bDuringRestore = bDuringRestore;
205 /*
206    // Call either Initialize for backup or for restore
207    if (m_bDuringRestore)  {
208       hr = m_pVssObject->InitializeForRestore(CComBSTR(L""));
209       if (FAILED(hr))
210          return FALSE;
211    }
212    else
213    {
214       // Initialize for backup
215       hr = m_pVssObject->InitializeForBackup();
216       if (FAILED(hr))
217          return FALSE;
218    }
219
220    
221
222    // Set various properties per backup components instance
223    hr = m_pVssObject->SetBackupState(true, true, VSS_BT_FULL, false);
224    if (FAILED(hr))
225       return FALSE;
226 */
227 // Keep the context
228    m_dwContext = dwContext;
229
230    return TRUE;
231 }
232
233
234 void VSSClient::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
235 {
236      // Wait until the async operation finishes
237     HRESULT hr = pAsync->Wait();
238
239     // Check the result of the asynchronous operation
240     HRESULT hrReturned = S_OK;
241     hr = pAsync->QueryStatus(&hrReturned, NULL);
242
243     // Check if the async operation succeeded...
244     if(FAILED(hrReturned))
245     {
246         throw(hrReturned);
247     }
248 }
249
250 BOOL VSSClient::CreateSnapshots(char* szDriveLetters)
251 {
252    /* szDriveLetters contains all drive letters in uppercase */
253    /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
254    /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
255    
256    if (!m_pVssObject || m_bBackupIsInitialized)
257       return FALSE;      
258
259    m_bBackupIsInitialized = true;
260
261    // 1. InitializeForBackup
262    HRESULT hr = m_pVssObject->InitializeForBackup();
263    if (FAILED(hr))
264       return FALSE;
265    
266    // 2. SetBackupState
267    hr = m_pVssObject->SetBackupState(true, true, VSS_BT_FULL, false);
268    if (FAILED(hr))
269       return FALSE;
270
271    CComPtr<IVssAsync>  pAsync;
272    VSS_ID latestSnapshotSetID;
273    VSS_ID pid;
274
275    // 3. startSnapshotSet
276
277    m_pVssObject->StartSnapshotSet(&latestSnapshotSetID);
278
279    // 4. AddToSnapshotSet
280
281    WCHAR szDrive[3];
282    szDrive[1] = ':';
283    szDrive[2] = 0;
284
285    wstring volume;
286
287    for (size_t i=0; i < strlen (szDriveLetters); i++) {
288       szDrive[0] = szDriveLetters[i];
289       volume = GetUniqueVolumeNameForPath(szDrive);
290       // store uniquevolumname
291       if (SUCCEEDED(m_pVssObject->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid)))
292          wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
293       else
294          szDriveLetters[i] = tolower (szDriveLetters[i]);               
295    }
296
297    // 5. PrepareForBackup
298
299    m_pVssObject->PrepareForBackup(&pAsync);
300
301    // Waits for the async operation to finish and checks the result
302    WaitAndCheckForAsyncOperation(pAsync);
303
304
305    // 6. DoSnapShotSet
306    
307    pAsync = NULL;
308    m_pVssObject->DoSnapshotSet(&pAsync);
309
310    // Waits for the async operation to finish and checks the result
311    WaitAndCheckForAsyncOperation(pAsync); 
312    
313    /* query snapshot info */
314    QuerySnapshotSet(latestSnapshotSetID);
315
316    return TRUE;
317 }
318
319 BOOL VSSClient::CloseBackup()
320 {
321    if (!m_pVssObject || !m_bBackupIsInitialized)
322       return FALSE;
323
324    m_bBackupIsInitialized = false;
325
326    CComPtr<IVssAsync>  pAsync;
327
328    if (SUCCEEDED(m_pVssObject->BackupComplete(&pAsync))) {
329      // Waits for the async operation to finish and checks the result
330      WaitAndCheckForAsyncOperation(pAsync);
331    }
332    else return FALSE;
333    
334    return TRUE;
335 }
336
337 // Query all the shadow copies in the given set
338 void VSSClient::QuerySnapshotSet(GUID snapshotSetID)
339 {   
340    memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
341    
342    if (snapshotSetID == GUID_NULL || m_pVssObject == NULL)
343       return;
344             
345    // Get list all shadow copies. 
346    CComPtr<IVssEnumObject> pIEnumSnapshots;
347    HRESULT hr = m_pVssObject->Query( GUID_NULL, 
348          VSS_OBJECT_NONE, 
349          VSS_OBJECT_SNAPSHOT, 
350          &pIEnumSnapshots );    
351
352    // If there are no shadow copies, just return
353    if (hr == S_FALSE) {
354       return;
355    } 
356
357    // Enumerate all shadow copies. 
358    VSS_OBJECT_PROP Prop;
359    VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
360    
361    while(true)
362    {
363       // Get the next element
364       ULONG ulFetched;
365       hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched );
366
367       // We reached the end of list
368       if (ulFetched == 0)
369          break;
370
371       // Print the shadow copy (if not filtered out)
372       if (Snap.m_SnapshotSetId == snapshotSetID)  {
373          for (char ch='A'-'A';ch<='Z'-'A';ch++) {
374             if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {               
375                WideCharToMultiByte(CP_UTF8,0,Snap.m_pwszSnapshotDeviceObject,-1,m_szShadowCopyName[ch],MAX_PATH*2,NULL,NULL);               
376                break;
377             }
378          }
379       }
380       ::VssFreeSnapshotProperties(&Snap);
381    }
382 }
383
384 BOOL VSSClient::GetShadowPath (const char* szFilePath, char* szShadowPath, int nBuflen)
385 {
386    /* check for valid pathname */
387    BOOL bIsValidName;
388    
389    bIsValidName = strlen (szFilePath) > 3;
390    if (bIsValidName)
391       bIsValidName &= isalpha (szFilePath[0]) &&
392                       szFilePath[1]==':' && 
393                       szFilePath[2]=='\\';
394
395    if (bIsValidName) {
396       int nDriveIndex = toupper(szFilePath[0])-'A';
397       if (m_szShadowCopyName[nDriveIndex][0] != 0) {
398          strncpy (szShadowPath, m_szShadowCopyName[nDriveIndex], nBuflen);
399          nBuflen -= (int) strlen (m_szShadowCopyName[nDriveIndex]);
400          strncat (szShadowPath, szFilePath+2,nBuflen);
401
402          return TRUE;
403       }
404    }
405    
406    strncpy (szShadowPath,  szFilePath, nBuflen);
407    return FALSE;   
408 }