]> wimlib.net Git - wimlib/blob - src/win32_vss.c
v1.14.4
[wimlib] / src / win32_vss.c
1 /*
2  * win32_vss.c - Windows-specific code for creating VSS (Volume Shadow Copy
3  * Service) snapshots.
4  */
5
6 /*
7  * Copyright (C) 2015-2023 Eric Biggers
8  *
9  * This file is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Lesser General Public License as published by the Free
11  * Software Foundation; either version 3 of the License, or (at your option) any
12  * later version.
13  *
14  * This file is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this file; if not, see https://www.gnu.org/licenses/.
21  */
22
23 #ifdef _WIN32
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
29 #include "wimlib/win32_common.h"
30
31 #include <cguid.h>
32
33 #include "wimlib/error.h"
34 #include "wimlib/threads.h"
35 #include "wimlib/util.h"
36 #include "wimlib/win32_vss.h"
37
38 /*----------------------------------------------------------------------------*
39  *                             VSS API declarations                           *
40  *----------------------------------------------------------------------------*/
41
42 typedef GUID VSS_ID;
43 typedef LONGLONG VSS_TIMESTAMP;
44 typedef WCHAR *VSS_PWSZ;
45
46 typedef enum {
47         VSS_BT_UNDEFINED      = 0,
48         VSS_BT_FULL           = 1,
49         VSS_BT_INCREMENTAL    = 2,
50         VSS_BT_DIFFERENTIAL   = 3,
51         VSS_BT_LOG            = 4,
52         VSS_BT_COPY           = 5,
53         VSS_BT_OTHER          = 6,
54 } VSS_BACKUP_TYPE;
55
56 typedef enum {
57         VSS_SS_UNKNOWN                      = 0x00,
58         VSS_SS_PREPARING                    = 0x01,
59         VSS_SS_PROCESSING_PREPARE           = 0x02,
60         VSS_SS_PREPARED                     = 0x03,
61         VSS_SS_PROCESSING_PRECOMMIT         = 0x04,
62         VSS_SS_PRECOMMITTED                 = 0x05,
63         VSS_SS_PROCESSING_COMMIT            = 0x06,
64         VSS_SS_COMMITTED                    = 0x07,
65         VSS_SS_PROCESSING_POSTCOMMIT        = 0x08,
66         VSS_SS_PROCESSING_PREFINALCOMMIT    = 0x09,
67         VSS_SS_PREFINALCOMMITTED            = 0x0a,
68         VSS_SS_PROCESSING_POSTFINALCOMMIT   = 0x0b,
69         VSS_SS_CREATED                      = 0x0c,
70         VSS_SS_ABORTED                      = 0x0d,
71         VSS_SS_DELETED                      = 0x0e,
72         VSS_SS_POSTCOMMITTED                = 0x0f,
73         VSS_SS_COUNT                        = 0x10,
74 } VSS_SNAPSHOT_STATE;
75
76 typedef enum {
77         VSS_VOLSNAP_ATTR_PERSISTENT             = 0x00000001,
78         VSS_VOLSNAP_ATTR_NO_AUTORECOVERY        = 0x00000002,
79         VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE      = 0x00000004,
80         VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE        = 0x00000008,
81         VSS_VOLSNAP_ATTR_NO_WRITERS             = 0x00000010,
82         VSS_VOLSNAP_ATTR_TRANSPORTABLE          = 0x00000020,
83         VSS_VOLSNAP_ATTR_NOT_SURFACED           = 0x00000040,
84         VSS_VOLSNAP_ATTR_NOT_TRANSACTED         = 0x00000080,
85         VSS_VOLSNAP_ATTR_HARDWARE_ASSISTED      = 0x00010000,
86         VSS_VOLSNAP_ATTR_DIFFERENTIAL           = 0x00020000,
87         VSS_VOLSNAP_ATTR_PLEX                   = 0x00040000,
88         VSS_VOLSNAP_ATTR_IMPORTED               = 0x00080000,
89         VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY        = 0x00100000,
90         VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY       = 0x00200000,
91         VSS_VOLSNAP_ATTR_AUTORECOVER            = 0x00400000,
92         VSS_VOLSNAP_ATTR_ROLLBACK_RECOVERY      = 0x00800000,
93         VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT   = 0x01000000,
94         VSS_VOLSNAP_ATTR_TXF_RECOVERY           = 0x02000000,
95 } VSS_VOLUME_SNAPSHOT_ATTRIBUTES;
96
97 typedef enum {
98         VSS_CTX_BACKUP                      = 0,
99         VSS_CTX_FILE_SHARE_BACKUP           = VSS_VOLSNAP_ATTR_NO_WRITERS,
100         VSS_CTX_NAS_ROLLBACK                = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ),
101         VSS_CTX_APP_ROLLBACK                = ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ),
102         VSS_CTX_CLIENT_ACCESSIBLE           = ( ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ),
103         VSS_CTX_CLIENT_ACCESSIBLE_WRITERS   = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ),
104         VSS_CTX_ALL                         = 0xffffffff,
105 } VSS_SNAPSHOT_CONTEXT;
106
107 typedef struct {
108         VSS_ID             m_SnapshotId;
109         VSS_ID             m_SnapshotSetId;
110         LONG               m_lSnapshotsCount;
111         VSS_PWSZ           m_pwszSnapshotDeviceObject;
112         VSS_PWSZ           m_pwszOriginalVolumeName;
113         VSS_PWSZ           m_pwszOriginatingMachine;
114         VSS_PWSZ           m_pwszServiceMachine;
115         VSS_PWSZ           m_pwszExposedName;
116         VSS_PWSZ           m_pwszExposedPath;
117         VSS_ID             m_ProviderId;
118         LONG               m_lSnapshotAttributes;
119         VSS_TIMESTAMP      m_tsCreationTimestamp;
120         VSS_SNAPSHOT_STATE m_eStatus;
121 } VSS_SNAPSHOT_PROP;
122
123 typedef struct IVssAsyncVTable IVssAsyncVTable;
124
125 typedef struct {
126         IVssAsyncVTable *vtable;
127 } IVssAsync;
128
129 struct IVssAsyncVTable {
130         void *QueryInterface;
131         void *AddRef;
132         ULONG (WINAPI *Release)(IVssAsync *this);
133         void *Cancel;
134         HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
135         void *QueryStatus;
136 };
137
138 typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable;
139
140 typedef struct {
141         IVssBackupComponentsVTable *vtable;
142 } IVssBackupComponents;
143
144 struct IVssBackupComponentsVTable {
145         void *QueryInterface;
146         void *AddRef;
147         ULONG (WINAPI *Release)(IVssBackupComponents *this);
148         void *GetWriterComponentsCount;
149         void *GetWriterComponents;
150         HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
151                                               BSTR bstrXML);
152         HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
153                                          BOOLEAN bSelectComponents,
154                                          BOOLEAN bBackupBootableSystemState,
155                                          VSS_BACKUP_TYPE backupType,
156                                          BOOLEAN bPartialFileSupport);
157         void *InitializeForRestore;
158         void *SetRestoreState;
159         HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
160                                                IVssAsync **ppAsync);
161         void *GetWriterMetadataCount;
162         void *GetWriterMetadata;
163         void *FreeWriterMetadata;
164         void *AddComponent;
165         HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
166                                            IVssAsync **ppAsync);
167         void *AbortBackup;
168         void *GatherWriterStatus;
169         void *GetWriterStatusCount;
170         void *FreeWriterStatus;
171         void *GetWriterStatus;
172         void *SetBackupSucceeded;
173         void *SetBackupOptions;
174         void *SetSelectedForRestore;
175         void *SetRestoreOptions;
176         void *SetAdditionalRestores;
177         void *SetPreviousBackupStamp;
178         void *SaveAsXML;
179         void *BackupComplete;
180         void *AddAlternativeLocationMapping;
181         void *AddRestoreSubcomponent;
182         void *SetFileRestoreStatus;
183         void *AddNewTarget;
184         void *SetRangesFilePath;
185         void *PreRestore;
186         void *PostRestore;
187         HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
188                                      LONG lContext);
189         HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
190                                            VSS_ID *pSnapshotSetId);
191         HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
192                                            VSS_PWSZ pwszVolumeName,
193                                            VSS_ID ProviderId,
194                                            VSS_ID *pidSnapshot);
195         HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
196                                         IVssAsync **ppAsync);
197         void *DeleteSnapshots;
198         void *ImportSnapshots;
199         void *BreakSnapshotSet;
200         HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
201                                                 VSS_ID SnapshotId,
202                                                 VSS_SNAPSHOT_PROP *pprop);
203         void *Query;
204         void *IsVolumeSupported;
205         void *DisableWriterClasses;
206         void *EnableWriterClasses;
207         void *DisableWriterInstances;
208         void *ExposeSnapshot;
209         void *RevertToSnapshot;
210         void *QueryRevertStatus;
211 };
212
213 /*----------------------------------------------------------------------------*
214  *                             VSS API initialization                         *
215  *----------------------------------------------------------------------------*/
216
217 static bool vss_initialized;
218 static struct mutex vss_initialization_mutex = MUTEX_INITIALIZER;
219
220 /* vssapi.dll  */
221 static HANDLE hVssapi;
222 static HRESULT (WINAPI *func_CreateVssBackupComponentsInternal)(IVssBackupComponents **ppBackup);
223 static void (WINAPI *func_VssFreeSnapshotPropertiesInternal)(VSS_SNAPSHOT_PROP *pProp);
224
225 /* ole32.dll  */
226 static HANDLE hOle32;
227 static void (WINAPI *func_CoInitialize)(LPVOID *pvReserved);
228 static void (WINAPI *func_CoUninitialize)(void);
229
230 static bool
231 vss_global_init_impl(void)
232 {
233         hVssapi = LoadLibrary(L"vssapi.dll");
234         if (!hVssapi) {
235                 ERROR("vssapi.dll not found");
236                 goto err;
237         }
238
239         func_CreateVssBackupComponentsInternal =
240                 (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal");
241         if (!func_CreateVssBackupComponentsInternal) {
242                 ERROR("CreateVssBackupComponentsInternal() not found in vssapi.dll");
243                 goto err_vssapi;
244         }
245
246         func_VssFreeSnapshotPropertiesInternal =
247                 (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal");
248         if (!func_VssFreeSnapshotPropertiesInternal) {
249                 ERROR("VssFreeSnapshotPropertiesInternal() not found in vssapi.dll");
250                 goto err_vssapi;
251         }
252
253         hOle32 = LoadLibrary(L"ole32.dll");
254         if (!hOle32) {
255                 ERROR("ole32.dll not found");
256                 goto err_vssapi;
257         }
258
259         func_CoInitialize = (void *)GetProcAddress(hOle32, "CoInitialize");
260         if (!func_CoInitialize) {
261                 ERROR("CoInitialize() not found in ole32.dll");
262                 goto err_ole32;
263         }
264
265         func_CoUninitialize = (void *)GetProcAddress(hOle32, "CoUninitialize");
266         if (!func_CoUninitialize) {
267                 ERROR("CoUninitialize() not found in ole32.dll");
268                 goto err_ole32;
269         }
270
271         (*func_CoInitialize)(NULL);
272         return true;
273
274 err_ole32:
275         FreeLibrary(hOle32);
276 err_vssapi:
277         FreeLibrary(hVssapi);
278 err:
279         return false;
280 }
281
282 static bool
283 vss_global_init(void)
284 {
285         if (vss_initialized)
286                 return true;
287
288         mutex_lock(&vss_initialization_mutex);
289         if (!vss_initialized)
290                 vss_initialized = vss_global_init_impl();
291         mutex_unlock(&vss_initialization_mutex);
292
293         if (vss_initialized)
294                 return true;
295         ERROR("The Volume Shadow Copy Service (VSS) API could not be "
296               "initialized.");
297         return false;
298 }
299
300 void
301 vss_global_cleanup(void)
302 {
303         if (!vss_initialized)
304                 return;
305
306         mutex_lock(&vss_initialization_mutex);
307         if (vss_initialized) {
308                 (*func_CoUninitialize)();
309                 FreeLibrary(hOle32);
310                 FreeLibrary(hVssapi);
311                 vss_initialized = false;
312         }
313         mutex_unlock(&vss_initialization_mutex);
314 }
315
316 /*----------------------------------------------------------------------------*
317  *                             VSS implementation                             *
318  *----------------------------------------------------------------------------*/
319
320 struct vss_snapshot_internal {
321         struct vss_snapshot base;
322         IVssBackupComponents *vss;
323         VSS_SNAPSHOT_PROP props;
324 };
325
326 /* Delete the specified VSS snapshot.  */
327 void
328 vss_delete_snapshot(struct vss_snapshot *snapshot)
329 {
330         struct vss_snapshot_internal *internal;
331
332         internal = container_of(snapshot, struct vss_snapshot_internal, base);
333
334         if (internal->props.m_pwszSnapshotDeviceObject)
335                 (*func_VssFreeSnapshotPropertiesInternal)(&internal->props);
336         if (internal->vss)
337                 internal->vss->vtable->Release(internal->vss);
338         FREE(internal);
339 }
340
341 static HRESULT
342 wait_and_release(IVssAsync *async)
343 {
344         HRESULT res = async->vtable->Wait(async, INFINITE);
345
346         async->vtable->Release(async);
347         return res;
348 }
349
350 static bool
351 request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
352                      VSS_ID *snapshot_id)
353 {
354         HRESULT res;
355         IVssAsync *async;
356
357         res = vss->vtable->InitializeForBackup(vss, NULL);
358         if (FAILED(res)) {
359                 ERROR("IVssBackupComponents.InitializeForBackup() error: %x",
360                       (u32)res);
361                 return false;
362         }
363
364         res = vss->vtable->SetBackupState(vss, FALSE, TRUE, VSS_BT_COPY, FALSE);
365         if (FAILED(res)) {
366                 ERROR("IVssBackupComponents.SetBackupState() error: %x",
367                       (u32)res);
368                 return false;
369         }
370
371         res = vss->vtable->StartSnapshotSet(vss, snapshot_id);
372         if (FAILED(res)) {
373                 ERROR("IVssBackupComponents.StartSnapshotSet() error: %x",
374                       (u32)res);
375                 return false;
376         }
377
378         res = vss->vtable->AddToSnapshotSet(vss, volume, (GUID){}, snapshot_id);
379         if (FAILED(res)) {
380                 ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x",
381                       (u32)res);
382                 return false;
383         }
384
385         res = vss->vtable->PrepareForBackup(vss, &async);
386         if (FAILED(res)) {
387                 ERROR("IVssBackupComponents.PrepareForBackup() error: %x",
388                       (u32)res);
389                 return false;
390         }
391         res = wait_and_release(async);
392         if (FAILED(res)) {
393                 ERROR("IVssAsync.Wait() error while preparing for backup: %x",
394                       (u32)res);
395                 return false;
396         }
397
398         res = vss->vtable->DoSnapshotSet(vss, &async);
399         if (FAILED(res)) {
400                 ERROR("IVssBackupComponents.DoSnapshotSet() error: %x",
401                       (u32)res);
402                 return false;
403         }
404         res = wait_and_release(async);
405         if (FAILED(res)) {
406                 ERROR("IVssAsync.Wait() error while doing snapshot set: %x",
407                       (u32)res);
408                 return false;
409         }
410
411         return true;
412 }
413
414 static bool
415 is_wow64(void)
416 {
417         BOOL wow64 = FALSE;
418         if (sizeof(size_t) == 4)
419                 IsWow64Process(GetCurrentProcess(), &wow64);
420         return wow64;
421 }
422
423 /*
424  * Create a VSS snapshot of the specified @volume.  Return the NT namespace path
425  * to the snapshot root directory in @vss_path_ret and a handle to the snapshot
426  * in @snapshot_ret.
427  */
428 int
429 vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
430                     struct vss_snapshot **snapshot_ret)
431 {
432         wchar_t *source_abspath;
433         wchar_t volume[4];
434         VSS_ID snapshot_id;
435         struct vss_snapshot_internal *snapshot = NULL;
436         IVssBackupComponents *vss;
437         HRESULT res;
438         int ret;
439
440         source_abspath = realpath(source, NULL);
441         if (!source_abspath) {
442                 ret = WIMLIB_ERR_NOMEM;
443                 goto err;
444         }
445
446         if (source_abspath[0] == L'\0' || source_abspath[1] != L':' ||
447             source_abspath[2] != L'\\') {
448                 ERROR("\"%ls\" (full path \"%ls\"): Path format not recognized",
449                       source, source_abspath);
450                 ret = WIMLIB_ERR_UNSUPPORTED;
451                 goto err;
452         }
453
454         wsprintf(volume, L"%lc:\\", source_abspath[0]);
455
456         snapshot = CALLOC(1, sizeof(*snapshot));
457         if (!snapshot) {
458                 ret = WIMLIB_ERR_NOMEM;
459                 goto err;
460         }
461
462         if (!vss_global_init())
463                 goto vss_err;
464
465         res = (*func_CreateVssBackupComponentsInternal)(&vss);
466         if (FAILED(res)) {
467                 ERROR("CreateVssBackupComponents error: %x", (u32)res);
468                 goto vss_err;
469         }
470
471         snapshot->vss = vss;
472
473         if (!request_vss_snapshot(vss, volume, &snapshot_id))
474                 goto vss_err;
475
476         res = vss->vtable->GetSnapshotProperties(vss, snapshot_id, &snapshot->props);
477         if (FAILED(res)) {
478                 ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x",
479                       (u32)res);
480                 goto vss_err;
481         }
482
483         if (wcsncmp(snapshot->props.m_pwszSnapshotDeviceObject, L"\\\\?\\", 4)) {
484                 ERROR("Unexpected volume shadow device path: %ls",
485                       snapshot->props.m_pwszSnapshotDeviceObject);
486                 goto vss_err;
487         }
488
489         vss_path_ret->MaximumLength = sizeof(wchar_t) *
490                 (wcslen(snapshot->props.m_pwszSnapshotDeviceObject) +
491                  1 + wcslen(&source_abspath[3]) + 1);
492         vss_path_ret->Length = vss_path_ret->MaximumLength - sizeof(wchar_t);
493         vss_path_ret->Buffer = HeapAlloc(GetProcessHeap(), 0,
494                                          vss_path_ret->MaximumLength);
495         if (!vss_path_ret->Buffer) {
496                 ret = WIMLIB_ERR_NOMEM;
497                 goto err;
498         }
499
500         wsprintf(vss_path_ret->Buffer, L"\\??\\%ls\\%ls",
501                  &snapshot->props.m_pwszSnapshotDeviceObject[4],
502                  &source_abspath[3]);
503         *snapshot_ret = &snapshot->base;
504         snapshot->base.refcnt = 1;
505         ret = 0;
506         goto out;
507
508 vss_err:
509         ret = WIMLIB_ERR_SNAPSHOT_FAILURE;
510         if (is_wow64()) {
511                 ERROR("64-bit Windows doesn't allow 32-bit applications to "
512                       "create VSS snapshots.\n"
513                       "        Run the 64-bit version of this application "
514                       "instead.");
515         } else {
516                 ERROR("A problem occurred while creating a VSS snapshot of "
517                       "\"%ls\".\n"
518                       "        Aborting the operation.", volume);
519         }
520 err:
521         if (snapshot)
522                 vss_delete_snapshot(&snapshot->base);
523 out:
524         FREE(source_abspath);
525         return ret;
526 }
527
528 #endif /* _WIN32 */