4ee6c9de0b83fb553a5be135df52291e8ee03ee8
[wimlib] / src / win32_vss.c
1 /*
2  * win32_vss.c - Experimental Windows-specific code for creating VSS (Volume
3  * Shadow Copy Service) snapshots.
4  */
5
6 /*
7  * Copyright (C) 2015 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 http://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 #include <pthread.h>
33
34 #include "wimlib/error.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         union {
135                 HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
136
137                 /* Pre-Vista version */
138                 HRESULT (WINAPI *OldWait)(IVssAsync *this);
139         };
140         void *QueryStatus;
141 };
142
143 typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable;
144 typedef struct IVssBackupComponentsVTable_old IVssBackupComponentsVTable_old;
145
146 typedef union {
147         IVssBackupComponentsVTable *vtable;
148         IVssBackupComponentsVTable_old *old_vtable;
149 } IVssBackupComponents;
150
151 struct IVssBackupComponentsVTable {
152         void *QueryInterface;
153         void *AddRef;
154         ULONG (WINAPI *Release)(IVssBackupComponents *this);
155         void *GetWriterComponentsCount;
156         void *GetWriterComponents;
157         HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
158                                               BSTR bstrXML);
159         HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
160                                          BOOLEAN bSelectComponents,
161                                          BOOLEAN bBackupBootableSystemState,
162                                          VSS_BACKUP_TYPE backupType,
163                                          BOOLEAN bPartialFileSupport);
164         void *InitializeForRestore;
165         void *SetRestoreState;
166         HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
167                                                IVssAsync **ppAsync);
168         void *GetWriterMetadataCount;
169         void *GetWriterMetadata;
170         void *FreeWriterMetadata;
171         void *AddComponent;
172         HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
173                                            IVssAsync **ppAsync);
174         void *AbortBackup;
175         void *GatherWriterStatus;
176         void *GetWriterStatusCount;
177         void *FreeWriterStatus;
178         void *GetWriterStatus;
179         void *SetBackupSucceeded;
180         void *SetBackupOptions;
181         void *SetSelectedForRestore;
182         void *SetRestoreOptions;
183         void *SetAdditionalRestores;
184         void *SetPreviousBackupStamp;
185         void *SaveAsXML;
186         void *BackupComplete;
187         void *AddAlternativeLocationMapping;
188         void *AddRestoreSubcomponent;
189         void *SetFileRestoreStatus;
190         void *AddNewTarget;
191         void *SetRangesFilePath;
192         void *PreRestore;
193         void *PostRestore;
194         HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
195                                      LONG lContext);
196         HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
197                                            VSS_ID *pSnapshotSetId);
198         HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
199                                            VSS_PWSZ pwszVolumeName,
200                                            VSS_ID ProviderId,
201                                            VSS_ID *pidSnapshot);
202         HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
203                                         IVssAsync **ppAsync);
204         void *DeleteSnapshots;
205         void *ImportSnapshots;
206         /*void *RemountReadWrite;*/     /* Old API only  */
207         void *BreakSnapshotSet;
208         HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
209                                                 VSS_ID SnapshotId,
210                                                 VSS_SNAPSHOT_PROP *pprop);
211         void *Query;
212         void *IsVolumeSupported;
213         void *DisableWriterClasses;
214         void *EnableWriterClasses;
215         void *DisableWriterInstances;
216         void *ExposeSnapshot;
217         void *RevertToSnapshot;
218         void *QueryRevertStatus;
219 };
220
221 /* Pre-Vista version  */
222 struct IVssBackupComponentsVTable_old {
223         void *QueryInterface;
224         void *AddRef;
225         ULONG (WINAPI *Release)(IVssBackupComponents *this);
226         void *GetWriterComponentsCount;
227         void *GetWriterComponents;
228         HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
229                                               BSTR bstrXML);
230         HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
231                                          BOOLEAN bSelectComponents,
232                                          BOOLEAN bBackupBootableSystemState,
233                                          VSS_BACKUP_TYPE backupType,
234                                          BOOLEAN bPartialFileSupport);
235         void *InitializeForRestore;
236         /*void *SetRestoreState;*/      /* New API only */
237         HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
238                                                IVssAsync **ppAsync);
239         void *GetWriterMetadataCount;
240         void *GetWriterMetadata;
241         void *FreeWriterMetadata;
242         void *AddComponent;
243         HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
244                                            IVssAsync **ppAsync);
245         void *AbortBackup;
246         void *GatherWriterStatus;
247         void *GetWriterStatusCount;
248         void *FreeWriterStatus;
249         void *GetWriterStatus;
250         void *SetBackupSucceeded;
251         void *SetBackupOptions;
252         void *SetSelectedForRestore;
253         void *SetRestoreOptions;
254         void *SetAdditionalRestores;
255         void *SetPreviousBackupStamp;
256         void *SaveAsXML;
257         void *BackupComplete;
258         void *AddAlternativeLocationMapping;
259         void *AddRestoreSubcomponent;
260         void *SetFileRestoreStatus;
261         /*void *AddNewTarget;*/         /* New API only */
262         /*void *SetRangesFilePath;*/    /* New API only */
263         void *PreRestore;
264         void *PostRestore;
265         HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
266                                      LONG lContext);
267         HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
268                                            VSS_ID *pSnapshotSetId);
269         HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
270                                            VSS_PWSZ pwszVolumeName,
271                                            VSS_ID ProviderId,
272                                            VSS_ID *pidSnapshot);
273         HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
274                                         IVssAsync **ppAsync);
275         void *DeleteSnapshots;
276         void *ImportSnapshots;
277         void *RemountReadWrite;
278         void *BreakSnapshotSet;
279         HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
280                                                 VSS_ID SnapshotId,
281                                                 VSS_SNAPSHOT_PROP *pprop);
282         void *Query;
283         void *IsVolumeSupported;
284         void *DisableWriterClasses;
285         void *EnableWriterClasses;
286         void *DisableWriterInstances;
287         void *ExposeSnapshot;
288         /*void *RevertToSnapshot;*/     /* New API only */
289         /*void *QueryRevertStatus;*/    /* New API only */
290 };
291
292 /* Call a method, assuming its signature is identical in the old and new APIs */
293 #define CALL_METHOD(obj, method, ...)                                   \
294 ({                                                                      \
295         HRESULT res;                                                    \
296         if (is_old_api)                                                 \
297                 res = (obj)->old_vtable->method((obj), ##__VA_ARGS__);  \
298         else                                                            \
299                 res = (obj)->vtable->method((obj), ##__VA_ARGS__);      \
300         res;                                                            \
301 })
302
303 /*----------------------------------------------------------------------------*
304  *                             VSS API initialization                         *
305  *----------------------------------------------------------------------------*/
306
307 static bool vss_initialized;
308 static pthread_mutex_t vss_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
309
310 /* vssapi.dll  */
311 static bool is_old_api;         /* old VSS API (pre-Vista)?  */
312 static HANDLE hVssapi;
313 static HRESULT (WINAPI *func_CreateVssBackupComponents)(IVssBackupComponents **ppBackup);
314 static void (WINAPI *func_VssFreeSnapshotProperties)(VSS_SNAPSHOT_PROP *pProp);
315
316 /* ole32.dll  */
317 static HANDLE hOle32;
318 static void (WINAPI *func_CoInitialize)(LPVOID *pvReserved);
319 static void (WINAPI *func_CoUninitialize)(void);
320
321 static bool
322 vss_global_init_impl(void)
323 {
324         hVssapi = LoadLibrary(L"vssapi.dll");
325         if (!hVssapi) {
326                 ERROR("vssapi.dll not found");
327                 goto err;
328         }
329
330         func_CreateVssBackupComponents =
331                 (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal");
332         if (!func_CreateVssBackupComponents) {
333                 func_CreateVssBackupComponents =
334                         (void *)GetProcAddress(hVssapi, "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z");
335                 if (!func_CreateVssBackupComponents) {
336                         ERROR("CreateVssBackupComponents() not found in vssapi.dll");
337                         goto err_vssapi;
338                 }
339                 is_old_api = true;
340         } else {
341                 is_old_api = false;
342         }
343
344         func_VssFreeSnapshotProperties =
345                 (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal");
346         if (!func_VssFreeSnapshotProperties) {
347                 func_VssFreeSnapshotProperties =
348                         (void *)GetProcAddress(hVssapi, "VssFreeSnapshotProperties");
349                 if (!func_VssFreeSnapshotProperties) {
350                         ERROR("VssFreeSnapshotProperties() not found in vssapi.dll");
351                         goto err_vssapi;
352                 }
353         }
354
355         hOle32 = LoadLibrary(L"ole32.dll");
356         if (!hOle32) {
357                 ERROR("ole32.dll not found");
358                 goto err_vssapi;
359         }
360
361         func_CoInitialize = (void *)GetProcAddress(hOle32, "CoInitialize");
362         if (!func_CoInitialize) {
363                 ERROR("CoInitialize() not found in ole32.dll");
364                 goto err_ole32;
365         }
366
367         func_CoUninitialize = (void *)GetProcAddress(hOle32, "CoUninitialize");
368         if (!func_CoUninitialize) {
369                 ERROR("CoUninitialize() not found in ole32.dll");
370                 goto err_ole32;
371         }
372
373         (*func_CoInitialize)(NULL);
374         return true;
375
376 err_ole32:
377         FreeLibrary(hOle32);
378 err_vssapi:
379         FreeLibrary(hVssapi);
380 err:
381         return false;
382 }
383
384 static bool
385 vss_global_init(void)
386 {
387         if (vss_initialized)
388                 return true;
389
390         pthread_mutex_lock(&vss_initialization_mutex);
391         if (!vss_initialized)
392                 vss_initialized = vss_global_init_impl();
393         pthread_mutex_unlock(&vss_initialization_mutex);
394
395         if (vss_initialized)
396                 return true;
397         ERROR("The Volume Shadow Copy Service (VSS) API could not be "
398               "initialized. Probably it isn't supported on this computer.");
399         return false;
400 }
401
402 void
403 vss_global_cleanup(void)
404 {
405         if (!vss_initialized)
406                 return;
407
408         pthread_mutex_lock(&vss_initialization_mutex);
409         if (vss_initialized) {
410                 (*func_CoUninitialize)();
411                 FreeLibrary(hOle32);
412                 FreeLibrary(hVssapi);
413                 vss_initialized = false;
414         }
415         pthread_mutex_unlock(&vss_initialization_mutex);
416 }
417
418 /*----------------------------------------------------------------------------*
419  *                             VSS implementation                             *
420  *----------------------------------------------------------------------------*/
421
422 struct vss_snapshot_internal {
423         struct vss_snapshot base;
424         IVssBackupComponents *vss;
425         VSS_SNAPSHOT_PROP props;
426 };
427
428 /* Delete the specified VSS snapshot.  */
429 void
430 vss_delete_snapshot(struct vss_snapshot *snapshot)
431 {
432         struct vss_snapshot_internal *internal;
433
434         internal = container_of(snapshot, struct vss_snapshot_internal, base);
435
436         if (internal->props.m_pwszSnapshotDeviceObject)
437                 (*func_VssFreeSnapshotProperties)(&internal->props);
438         if (internal->vss)
439                 CALL_METHOD(internal->vss, Release);
440         FREE(internal);
441 }
442
443 static HRESULT
444 wait_and_release(IVssAsync *async)
445 {
446         HRESULT res;
447         if (is_old_api)
448                 res = async->vtable->OldWait(async);
449         else
450                 res = async->vtable->Wait(async, INFINITE);
451         async->vtable->Release(async);
452         return res;
453 }
454
455 static bool
456 request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
457                      VSS_ID *snapshot_id)
458 {
459         HRESULT res;
460         IVssAsync *async;
461
462         res = CALL_METHOD(vss, InitializeForBackup, NULL);
463         if (FAILED(res)) {
464                 ERROR("IVssBackupComponents.InitializeForBackup() error: %x", res);
465                 return false;
466         }
467
468         res = CALL_METHOD(vss, SetBackupState, FALSE, TRUE, VSS_BT_COPY, FALSE);
469         if (FAILED(res)) {
470                 ERROR("IVssBackupComponents.SetBackupState() error: %x", res);
471                 return false;
472         }
473
474         res = CALL_METHOD(vss, StartSnapshotSet, snapshot_id);
475         if (FAILED(res)) {
476                 ERROR("IVssBackupComponents.StartSnapshotSet() error: %x", res);
477                 return false;
478         }
479
480         res = CALL_METHOD(vss, AddToSnapshotSet, volume, (GUID){}, snapshot_id);
481         if (FAILED(res)) {
482                 ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x", res);
483                 return false;
484         }
485
486         res = CALL_METHOD(vss, PrepareForBackup, &async);
487         if (FAILED(res)) {
488                 ERROR("IVssBackupComponents.PrepareForBackup() error: %x", res);
489                 return false;
490         }
491         res = wait_and_release(async);
492         if (FAILED(res)) {
493                 ERROR("IVssAsync.Wait() error while preparing for backup: %x", res);
494                 return false;
495         }
496
497         res = CALL_METHOD(vss, DoSnapshotSet, &async);
498         if (FAILED(res)) {
499                 ERROR("IVssBackupComponents.DoSnapshotSet() error: %x", res);
500                 return false;
501         }
502         res = wait_and_release(async);
503         if (FAILED(res)) {
504                 ERROR("IVssAsync.Wait() error while doing snapshot set: %x", res);
505                 return false;
506         }
507
508         return true;
509 }
510
511 static bool
512 is_wow64(void)
513 {
514         BOOL wow64 = FALSE;
515         if (sizeof(size_t) == 4)
516                 IsWow64Process(GetCurrentProcess(), &wow64);
517         return wow64;
518 }
519
520 /*
521  * Create a VSS snapshot of the specified @volume.  Return the NT namespace path
522  * to the snapshot root directory in @vss_path_ret and a handle to the snapshot
523  * in @snapshot_ret.
524  */
525 int
526 vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
527                     struct vss_snapshot **snapshot_ret)
528 {
529         wchar_t *source_abspath;
530         wchar_t volume[4];
531         VSS_ID snapshot_id;
532         struct vss_snapshot_internal *snapshot = NULL;
533         IVssBackupComponents *vss;
534         HRESULT res;
535         int ret;
536
537         source_abspath = realpath(source, NULL);
538         if (!source_abspath) {
539                 ret = WIMLIB_ERR_NOMEM;
540                 goto err;
541         }
542
543         if (source_abspath[0] == L'\0' || source_abspath[1] != L':' ||
544             source_abspath[2] != L'\\') {
545                 ERROR("\"%ls\" (full path \"%ls\"): Path format not recognized",
546                       source, source_abspath);
547                 ret = WIMLIB_ERR_UNSUPPORTED;
548                 goto err;
549         }
550
551         wsprintf(volume, L"%lc:\\", source_abspath[0]);
552
553         snapshot = CALLOC(1, sizeof(*snapshot));
554         if (!snapshot) {
555                 ret = WIMLIB_ERR_NOMEM;
556                 goto err;
557         }
558
559         if (!vss_global_init())
560                 goto vss_err;
561
562         res = (*func_CreateVssBackupComponents)(&vss);
563         if (FAILED(res)) {
564                 ERROR("CreateVssBackupComponents error: %x", res);
565                 goto vss_err;
566         }
567
568         snapshot->vss = vss;
569
570         if (!request_vss_snapshot(vss, volume, &snapshot_id))
571                 goto vss_err;
572
573         res = CALL_METHOD(vss, GetSnapshotProperties, snapshot_id, &snapshot->props);
574         if (FAILED(res)) {
575                 ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x", res);
576                 goto vss_err;
577         }
578
579         if (wcsncmp(snapshot->props.m_pwszSnapshotDeviceObject, L"\\\\?\\", 4)) {
580                 ERROR("Unexpected volume shadow device path: %ls",
581                       snapshot->props.m_pwszSnapshotDeviceObject);
582                 goto vss_err;
583         }
584
585         vss_path_ret->MaximumLength = sizeof(wchar_t) *
586                 (wcslen(snapshot->props.m_pwszSnapshotDeviceObject) +
587                  1 + wcslen(&source_abspath[3]) + 1);
588         vss_path_ret->Length = vss_path_ret->MaximumLength - sizeof(wchar_t);
589         vss_path_ret->Buffer = HeapAlloc(GetProcessHeap(), 0,
590                                          vss_path_ret->MaximumLength);
591         if (!vss_path_ret->Buffer) {
592                 ret = WIMLIB_ERR_NOMEM;
593                 goto err;
594         }
595
596         wsprintf(vss_path_ret->Buffer, L"\\??\\%ls\\%ls",
597                  &snapshot->props.m_pwszSnapshotDeviceObject[4],
598                  &source_abspath[3]);
599         *snapshot_ret = &snapshot->base;
600         snapshot->base.refcnt = 1;
601         ret = 0;
602         goto out;
603
604 vss_err:
605         ret = WIMLIB_ERR_SNAPSHOT_FAILURE;
606         if (is_wow64()) {
607                 ERROR("64-bit Windows doesn't allow 32-bit applications to "
608                       "create VSS snapshots.\n"
609                       "        Run the 64-bit version of this application "
610                       "instead.");
611         } else {
612                 ERROR("A problem occurred while creating a VSS snapshot of "
613                       "\"%ls\".\n"
614                       "        Aborting the operation.", volume);
615         }
616 err:
617         if (snapshot)
618                 vss_delete_snapshot(&snapshot->base);
619 out:
620         FREE(source_abspath);
621         return ret;
622 }
623
624 #endif /* __WIN32__ */