vss: support on Windows XP
authorEric Biggers <ebiggers3@gmail.com>
Sat, 2 Jan 2016 19:30:01 +0000 (13:30 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 2 Jan 2016 19:43:28 +0000 (13:43 -0600)
doc/man1/wimlib-imagex-capture.1
include/wimlib.h
src/win32_vss.c

index 6198d52..0519700 100644 (file)
@@ -579,13 +579,13 @@ See the documentation for this option in \fBwimlib-imagex-optimize\fR (1).
 .TP
 \fB--snapshot\fR
 EXPERIMENTAL: create a temporary filesystem snapshot of the source directory and
-capture the files from it.  Currently, this option is only supported on Windows
-(Vista and later), where it uses the Volume Shadow Copy Service (VSS).  Using
-this option, you can create a consistent backup of the system volume of a
-running Windows system without running into problems with locked files.  For the
-VSS snapshot to be successfully created, \fBwimlib-imagex\fR must be run as an
-Administrator, and it cannot be run in WoW64 mode (i.e. if Windows is 64-bit,
-then \fBwimlib-imagex\fR must be 64-bit as well).
+capture the files from it.  Currently, this option is only supported on Windows,
+where it uses the Volume Shadow Copy Service (VSS).  Using this option, you can
+create a consistent backup of the system volume of a running Windows system
+without running into problems with locked files.  For the VSS snapshot to be
+successfully created, \fBwimlib-imagex\fR must be run as an Administrator, and
+it cannot be run in WoW64 mode (i.e. if Windows is 64-bit, then
+\fBwimlib-imagex\fR must be 64-bit as well).
 .SH NOTES
 \fBwimlib-imagex append\fR does not support appending an image to a split WIM.
 .PP
index f2e9f22..2e5ba3c 100644 (file)
@@ -1750,13 +1750,12 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
 /**
  * EXPERIMENTAL, since wimlib v1.9.0: create a temporary filesystem snapshot of
  * the source directory and add the files from it.  Currently, this option is
- * only supported on Windows (Vista and later), where it uses the Volume Shadow
- * Copy Service (VSS).  Using this option, you can create a consistent backup of
- * the system volume of a running Windows system without running into problems
- * with locked files.  For the VSS snapshot to be successfully created, your
- * application must be run as an Administrator, and it cannot be run in WoW64
- * mode (i.e. if Windows is 64-bit, then your application must be 64-bit as
- * well).
+ * only supported on Windows, where it uses the Volume Shadow Copy Service
+ * (VSS).  Using this option, you can create a consistent backup of the system
+ * volume of a running Windows system without running into problems with locked
+ * files.  For the VSS snapshot to be successfully created, your application
+ * must be run as an Administrator, and it cannot be run in WoW64 mode (i.e. if
+ * Windows is 64-bit, then your application must be 64-bit as well).
  */
 #define WIMLIB_ADD_FLAG_SNAPSHOT               0x00008000
 
index 46af4bb..4ee6c9d 100644 (file)
@@ -131,14 +131,21 @@ struct IVssAsyncVTable {
        void *AddRef;
        ULONG (WINAPI *Release)(IVssAsync *this);
        void *Cancel;
-       HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
+       union {
+               HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
+
+               /* Pre-Vista version */
+               HRESULT (WINAPI *OldWait)(IVssAsync *this);
+       };
        void *QueryStatus;
 };
 
 typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable;
+typedef struct IVssBackupComponentsVTable_old IVssBackupComponentsVTable_old;
 
-typedef struct {
+typedef union {
        IVssBackupComponentsVTable *vtable;
+       IVssBackupComponentsVTable_old *old_vtable;
 } IVssBackupComponents;
 
 struct IVssBackupComponentsVTable {
@@ -196,6 +203,7 @@ struct IVssBackupComponentsVTable {
                                        IVssAsync **ppAsync);
        void *DeleteSnapshots;
        void *ImportSnapshots;
+       /*void *RemountReadWrite;*/     /* Old API only  */
        void *BreakSnapshotSet;
        HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
                                                VSS_ID SnapshotId,
@@ -210,6 +218,88 @@ struct IVssBackupComponentsVTable {
        void *QueryRevertStatus;
 };
 
+/* Pre-Vista version  */
+struct IVssBackupComponentsVTable_old {
+       void *QueryInterface;
+       void *AddRef;
+       ULONG (WINAPI *Release)(IVssBackupComponents *this);
+       void *GetWriterComponentsCount;
+       void *GetWriterComponents;
+       HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
+                                             BSTR bstrXML);
+       HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
+                                        BOOLEAN bSelectComponents,
+                                        BOOLEAN bBackupBootableSystemState,
+                                        VSS_BACKUP_TYPE backupType,
+                                        BOOLEAN bPartialFileSupport);
+       void *InitializeForRestore;
+       /*void *SetRestoreState;*/      /* New API only */
+       HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
+                                              IVssAsync **ppAsync);
+       void *GetWriterMetadataCount;
+       void *GetWriterMetadata;
+       void *FreeWriterMetadata;
+       void *AddComponent;
+       HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
+                                          IVssAsync **ppAsync);
+       void *AbortBackup;
+       void *GatherWriterStatus;
+       void *GetWriterStatusCount;
+       void *FreeWriterStatus;
+       void *GetWriterStatus;
+       void *SetBackupSucceeded;
+       void *SetBackupOptions;
+       void *SetSelectedForRestore;
+       void *SetRestoreOptions;
+       void *SetAdditionalRestores;
+       void *SetPreviousBackupStamp;
+       void *SaveAsXML;
+       void *BackupComplete;
+       void *AddAlternativeLocationMapping;
+       void *AddRestoreSubcomponent;
+       void *SetFileRestoreStatus;
+       /*void *AddNewTarget;*/         /* New API only */
+       /*void *SetRangesFilePath;*/    /* New API only */
+       void *PreRestore;
+       void *PostRestore;
+       HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
+                                    LONG lContext);
+       HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
+                                          VSS_ID *pSnapshotSetId);
+       HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
+                                          VSS_PWSZ pwszVolumeName,
+                                          VSS_ID ProviderId,
+                                          VSS_ID *pidSnapshot);
+       HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
+                                       IVssAsync **ppAsync);
+       void *DeleteSnapshots;
+       void *ImportSnapshots;
+       void *RemountReadWrite;
+       void *BreakSnapshotSet;
+       HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
+                                               VSS_ID SnapshotId,
+                                               VSS_SNAPSHOT_PROP *pprop);
+       void *Query;
+       void *IsVolumeSupported;
+       void *DisableWriterClasses;
+       void *EnableWriterClasses;
+       void *DisableWriterInstances;
+       void *ExposeSnapshot;
+       /*void *RevertToSnapshot;*/     /* New API only */
+       /*void *QueryRevertStatus;*/    /* New API only */
+};
+
+/* Call a method, assuming its signature is identical in the old and new APIs */
+#define CALL_METHOD(obj, method, ...)                                  \
+({                                                                     \
+       HRESULT res;                                                    \
+       if (is_old_api)                                                 \
+               res = (obj)->old_vtable->method((obj), ##__VA_ARGS__);  \
+       else                                                            \
+               res = (obj)->vtable->method((obj), ##__VA_ARGS__);      \
+       res;                                                            \
+})
+
 /*----------------------------------------------------------------------------*
  *                             VSS API initialization                         *
  *----------------------------------------------------------------------------*/
@@ -218,9 +308,10 @@ static bool vss_initialized;
 static pthread_mutex_t vss_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /* vssapi.dll  */
+static bool is_old_api;                /* old VSS API (pre-Vista)?  */
 static HANDLE hVssapi;
-static HRESULT (WINAPI *func_CreateVssBackupComponentsInternal)(IVssBackupComponents **ppBackup);
-static void (WINAPI *func_VssFreeSnapshotPropertiesInternal)(VSS_SNAPSHOT_PROP *pProp);
+static HRESULT (WINAPI *func_CreateVssBackupComponents)(IVssBackupComponents **ppBackup);
+static void (WINAPI *func_VssFreeSnapshotProperties)(VSS_SNAPSHOT_PROP *pProp);
 
 /* ole32.dll  */
 static HANDLE hOle32;
@@ -236,18 +327,29 @@ vss_global_init_impl(void)
                goto err;
        }
 
-       func_CreateVssBackupComponentsInternal =
+       func_CreateVssBackupComponents =
                (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal");
-       if (!func_CreateVssBackupComponentsInternal) {
-               ERROR("CreateVssBackupComponentsInternal() not found in vssapi.dll");
-               goto err_vssapi;
+       if (!func_CreateVssBackupComponents) {
+               func_CreateVssBackupComponents =
+                       (void *)GetProcAddress(hVssapi, "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z");
+               if (!func_CreateVssBackupComponents) {
+                       ERROR("CreateVssBackupComponents() not found in vssapi.dll");
+                       goto err_vssapi;
+               }
+               is_old_api = true;
+       } else {
+               is_old_api = false;
        }
 
-       func_VssFreeSnapshotPropertiesInternal =
+       func_VssFreeSnapshotProperties =
                (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal");
-       if (!func_VssFreeSnapshotPropertiesInternal) {
-               ERROR("VssFreeSnapshotPropertiesInternal() not found in vssapi.dll");
-               goto err_vssapi;
+       if (!func_VssFreeSnapshotProperties) {
+               func_VssFreeSnapshotProperties =
+                       (void *)GetProcAddress(hVssapi, "VssFreeSnapshotProperties");
+               if (!func_VssFreeSnapshotProperties) {
+                       ERROR("VssFreeSnapshotProperties() not found in vssapi.dll");
+                       goto err_vssapi;
+               }
        }
 
        hOle32 = LoadLibrary(L"ole32.dll");
@@ -293,7 +395,7 @@ vss_global_init(void)
        if (vss_initialized)
                return true;
        ERROR("The Volume Shadow Copy Service (VSS) API could not be "
-             "initialized.");
+             "initialized. Probably it isn't supported on this computer.");
        return false;
 }
 
@@ -332,16 +434,20 @@ vss_delete_snapshot(struct vss_snapshot *snapshot)
        internal = container_of(snapshot, struct vss_snapshot_internal, base);
 
        if (internal->props.m_pwszSnapshotDeviceObject)
-               (*func_VssFreeSnapshotPropertiesInternal)(&internal->props);
+               (*func_VssFreeSnapshotProperties)(&internal->props);
        if (internal->vss)
-               internal->vss->vtable->Release(internal->vss);
+               CALL_METHOD(internal->vss, Release);
        FREE(internal);
 }
 
 static HRESULT
 wait_and_release(IVssAsync *async)
 {
-       HRESULT res = async->vtable->Wait(async, INFINITE);
+       HRESULT res;
+       if (is_old_api)
+               res = async->vtable->OldWait(async);
+       else
+               res = async->vtable->Wait(async, INFINITE);
        async->vtable->Release(async);
        return res;
 }
@@ -353,37 +459,31 @@ request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
        HRESULT res;
        IVssAsync *async;
 
-       res = vss->vtable->InitializeForBackup(vss, NULL);
+       res = CALL_METHOD(vss, InitializeForBackup, NULL);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.InitializeForBackup() error: %x", res);
                return false;
        }
 
-       res = vss->vtable->SetContext(vss, VSS_CTX_BACKUP);
-       if (FAILED(res)) {
-               ERROR("IVssBackupComponents.SetContext() error: %x", res);
-               return false;
-       }
-
-       res = vss->vtable->SetBackupState(vss, FALSE, TRUE, VSS_BT_COPY, FALSE);
+       res = CALL_METHOD(vss, SetBackupState, FALSE, TRUE, VSS_BT_COPY, FALSE);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.SetBackupState() error: %x", res);
                return false;
        }
 
-       res = vss->vtable->StartSnapshotSet(vss, snapshot_id);
+       res = CALL_METHOD(vss, StartSnapshotSet, snapshot_id);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.StartSnapshotSet() error: %x", res);
                return false;
        }
 
-       res = vss->vtable->AddToSnapshotSet(vss, volume, (GUID){}, snapshot_id);
+       res = CALL_METHOD(vss, AddToSnapshotSet, volume, (GUID){}, snapshot_id);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x", res);
                return false;
        }
 
-       res = vss->vtable->PrepareForBackup(vss, &async);
+       res = CALL_METHOD(vss, PrepareForBackup, &async);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.PrepareForBackup() error: %x", res);
                return false;
@@ -394,7 +494,7 @@ request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
                return false;
        }
 
-       res = vss->vtable->DoSnapshotSet(vss, &async);
+       res = CALL_METHOD(vss, DoSnapshotSet, &async);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.DoSnapshotSet() error: %x", res);
                return false;
@@ -409,12 +509,6 @@ request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
 }
 
 static bool
-is_pre_vista(void)
-{
-       return (GetVersion() & 0xFF) < 6;
-}
-
-static bool
 is_wow64(void)
 {
        BOOL wow64 = FALSE;
@@ -465,7 +559,7 @@ vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
        if (!vss_global_init())
                goto vss_err;
 
-       res = (*func_CreateVssBackupComponentsInternal)(&vss);
+       res = (*func_CreateVssBackupComponents)(&vss);
        if (FAILED(res)) {
                ERROR("CreateVssBackupComponents error: %x", res);
                goto vss_err;
@@ -476,7 +570,7 @@ vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
        if (!request_vss_snapshot(vss, volume, &snapshot_id))
                goto vss_err;
 
-       res = vss->vtable->GetSnapshotProperties(vss, snapshot_id, &snapshot->props);
+       res = CALL_METHOD(vss, GetSnapshotProperties, snapshot_id, &snapshot->props);
        if (FAILED(res)) {
                ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x", res);
                goto vss_err;
@@ -509,10 +603,7 @@ vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
 
 vss_err:
        ret = WIMLIB_ERR_SNAPSHOT_FAILURE;
-       if (is_pre_vista() && !vss_initialized) {
-               ERROR("Snapshot mode is only supported on Windows Vista "
-                     "and later!");
-       } else if (is_wow64()) {
+       if (is_wow64()) {
                ERROR("64-bit Windows doesn't allow 32-bit applications to "
                      "create VSS snapshots.\n"
                      "        Run the 64-bit version of this application "