]> git.sur5r.net Git - bacula/bacula/commitdiff
improved vss support (goal: only 1 binary, still problems on w2k3, xp seems to work)
authorThorsten Engel <thorsten.engel@matrix-computer.com>
Tue, 24 May 2005 14:25:57 +0000 (14:25 +0000)
committerThorsten Engel <thorsten.engel@matrix-computer.com>
Tue, 24 May 2005 14:25:57 +0000 (14:25 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2081 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/src/filed/job.c
bacula/src/lib/winapi.c
bacula/src/lib/winapi.h
bacula/src/win32/compat/compat.cpp
bacula/src/win32/compat/vss.cpp
bacula/src/win32/compat/vss.h
bacula/src/win32/compat/vss_W2K3.cpp [new file with mode: 0644]
bacula/src/win32/compat/vss_XP.cpp [new file with mode: 0644]
bacula/src/win32/compat/vss_generic.cpp [new file with mode: 0644]

index b6f02c51b99ccb70654c627f99b8453c2165dc12..7d3edd1b8b3284671c44c732fea3731d2319bd6e 100644 (file)
@@ -1204,17 +1204,27 @@ static int backup_cmd(JCR *jcr)
 
 #ifdef WIN32_VSS
    /* START VSS ON WIN 32 */
-   g_VSSClient.InitializeForBackup();
-   /* tell vss which drives to snapshot */   
-   char szWinDriveLetters[27];   
-   if (get_win32_driveletters((FF_PKT *)jcr->ff, szWinDriveLetters)) {
-      Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Drives=%s\n"), szWinDriveLetters);
-      g_VSSClient.CreateSnapshots(szWinDriveLetters);
-
-      for (int i=0; i<strlen (szWinDriveLetters); i++) {
-         if (islower(szWinDriveLetters[i]))
-            Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshot of drive %c: failed\n"), szWinDriveLetters[i]);
+   if (g_pVSSClient) {
+      if (g_pVSSClient->InitializeForBackup()) {
+         /* tell vss which drives to snapshot */   
+         char szWinDriveLetters[27];   
+         if (get_win32_driveletters((FF_PKT *)jcr->ff, szWinDriveLetters)) {
+            Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=%s, Drive(s)=%s\n"), g_pVSSClient->GetDriverName(), szWinDriveLetters);
+
+            if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters)) {
+                  Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshots failed\n"));
+            }
+            else {
+               for (int i=0; i<strlen (szWinDriveLetters); i++) {
+                  if (islower(szWinDriveLetters[i]))
+                     Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshot of drive %c: failed\n"), szWinDriveLetters[i]);
+               }
+            }
+         }
+      } else {
+         Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled."));
       }
+
    }
 #endif
 
@@ -1274,7 +1284,8 @@ static int backup_cmd(JCR *jcr)
 cleanup:
 #ifdef WIN32_VSS
    /* tell vss to close the backup session */
-   g_VSSClient.CloseBackup();
+   if (g_pVSSClient)
+      g_pVSSClient->CloseBackup();
 #endif
 
    bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->JobFiles,
index b7bf9d613ea8e4e3ac2af8ac0872e59c5d8e8e0a..0f640a49adeb3852c7867a1b156943cf4166e3d2 100644 (file)
 
 #include "winapi.h"
 
+#ifdef WIN32_VSS
+#include "vss.h"   
+#endif
+
 // init with win9x, but maybe set to NT in InitWinAPI
 DWORD  g_platform_id = VER_PLATFORM_WIN32_WINDOWS;
+/* preset VSSClient to NULL */
+VSSClient* g_pVSSClient = NULL;
 
 
 /* API Pointers */
@@ -75,6 +81,14 @@ t_SetCurrentDirectoryW p_SetCurrentDirectoryW = NULL;
 t_GetCurrentDirectoryA p_GetCurrentDirectoryA = NULL;
 t_GetCurrentDirectoryW p_GetCurrentDirectoryW = NULL;
 
+#ifdef WIN32_VSS
+void 
+VSSCleanup()
+{
+   if (g_pVSSClient)
+      delete (g_pVSSClient);
+}
+#endif
 
 void 
 InitWinAPIWrapper() 
@@ -162,16 +176,21 @@ InitWinAPIWrapper()
    }
 
    // do we run on win 9x ???
-   OSVERSIONINFO osversioninfo;
+   OSVERSIONINFO osversioninfo;   
    osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
 
+   DWORD dwMinorVersion;
+
    // Get the current OS version
    if (!GetVersionEx(&osversioninfo)) {
       g_platform_id = 0;
+      dwMinorVersion = 0;
    } else {
       g_platform_id = osversioninfo.dwPlatformId;
+      dwMinorVersion = osversioninfo.dwMinorVersion;
    }
 
+   /* deinitialize some routines on win95 - they're not implemented well */
    if (g_platform_id == VER_PLATFORM_WIN32_WINDOWS) {
       p_BackupRead = NULL;
       p_BackupWrite = NULL;
@@ -191,5 +210,20 @@ InitWinAPIWrapper()
       p_wmkdir = NULL;
       p_wopen = NULL;
    }   
+
+   /* decide which vss class to initialize */
+#ifdef WIN32_VSS
+   switch (dwMinorVersion) {
+      case 1: 
+         g_pVSSClient = new VSSClientXP();
+         atexit(VSSCleanup);
+         break;
+      case 2: 
+         g_pVSSClient = new VSSClient2003();
+         atexit(VSSCleanup);
+         break;
+   }
+#endif
 }
+
 #endif
index ed3580a416484b8153ef4ecee13ef2a0e485b233..1b815d51d9fb8299570965ac1e020c40072e5390 100644 (file)
@@ -140,8 +140,8 @@ extern t_GetCurrentDirectoryA p_GetCurrentDirectoryA;
 extern t_GetCurrentDirectoryW p_GetCurrentDirectoryW;
 
 #ifdef WIN32_VSS
-class VSSClient;
-extern VSSClient g_VSSClient;
+class  VSSClient;
+extern VSSClient* g_pVSSClient;
 #endif
 
 void InitWinAPIWrapper();
index 840c0404ddf6165a05a78341071b38b80177e15e..18d1ec6ec4cd0a3852d759e4fb8ec82e08f46811 100644 (file)
@@ -84,12 +84,14 @@ cygwin_conv_to_win32_path(const char *name, char *win32_name, DWORD dwSize)
        can get longer because VSS will make something like
        \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\bacula\\uninstall.exe
        from c:\bacula\uninstall.exe
-    */    
-    POOLMEM* pszBuf = get_pool_memory (PM_FNAME);
-    pszBuf = check_pool_memory_size(pszBuf, dwSize);
-    bstrncpy (pszBuf, tname, strlen(tname)+1);
-    g_VSSClient.GetShadowPath(pszBuf,tname,dwSize);
-    free_pool_memory(pszBuf);
+    */ 
+    if (g_pVSSClient) {
+      POOLMEM* pszBuf = get_pool_memory (PM_FNAME);
+      pszBuf = check_pool_memory_size(pszBuf, dwSize);
+      bstrncpy (pszBuf, tname, strlen(tname)+1);
+      g_pVSSClient->GetShadowPath(pszBuf,tname,dwSize);
+      free_pool_memory(pszBuf);
+    }
 #endif
 }
 
index 6177b06c61a9a7498a0d961148f41ff20ccfbf66..523cc68f21bd8b1f4e08f2ccfeef78dd7d9aef89 100644 (file)
@@ -65,74 +65,18 @@ using namespace std;
 
 // Used for safe string manipulation
 #include <strsafe.h>
-
-#include "vss/inc/WinXP/vss.h"
-#include "vss/inc/WinXP/vswriter.h"
-#include "vss/inc/WinXP/vsbackup.h"
-
 #include "vss.h"
 
-#pragma comment(lib,"C:/Development/bacula/bacula/src/win32/compat/vss/lib/WinXP/obj/i386/vssapi.lib")
-#pragma comment(lib,"C:/Development/bacula/bacula/src/win32/compat/vss/lib/WinXP/obj/i386/vss_uuid.lib")
-#pragma comment(lib,"atlsd.lib")
-
-
-
-// define global VssClient
-VSSClient g_VSSClient;
-
-/* 
- *
- * some helper functions 
- *
- *
- */
-
-// Append a backslash to the current string 
-inline wstring AppendBackslash(wstring str)
-{
-    if (str.length() == 0)
-        return wstring(L"\\");
-    if (str[str.length() - 1] == L'\\')
-        return str;
-    return str.append(L"\\");
-}
-
-// Get the unique volume name for the given path
-inline wstring GetUniqueVolumeNameForPath(wstring path)
-{
-    _ASSERTE(path.length() > 0);
-
-    // Add the backslash termination, if needed
-    path = AppendBackslash(path);
-
-    // Get the root path of the volume
-    WCHAR volumeRootPath[MAX_PATH];
-    WCHAR volumeName[MAX_PATH];
-    WCHAR volumeUniqueName[MAX_PATH];
 
-    if (!GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
-      return L'\0';
-    
-    // Get the volume name alias (might be different from the unique volume name in rare cases)
-    if (!GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
-       return L'\0';
-    
-    // Get the unique volume name    
-    if (!GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
-       return L'\0';
-    
-    return volumeUniqueName;
-}
+#pragma comment(lib,"atlsd.lib")
 
 
-// we need something like a map
 
 // Constructor
 VSSClient::VSSClient()
 {
     m_bCoInitializeCalled = false;
-    m_dwContext = VSS_CTX_BACKUP;
+    m_dwContext = 0; // VSS_CTX_BACKUP;
     m_bDuringRestore = false;
     m_bBackupIsInitialized = false;
     m_pVssObject = NULL;
@@ -157,232 +101,18 @@ VSSClient::~VSSClient()
 
 BOOL VSSClient::InitializeForBackup()
 {
-    return Initialize (VSS_CTX_BACKUP);
-}
-
-// Initialize the COM infrastructure and the internal pointers
-BOOL VSSClient::Initialize(DWORD dwContext, bool bDuringRestore)
-{
-   HRESULT hr;
-   // Initialize COM 
-   if (!m_bCoInitializeCalled)  {
-      if (FAILED(CoInitialize(NULL)))
-         return FALSE;
-
-      m_bCoInitializeCalled = true;
-
-      // Initialize COM security
-      hr =
-         CoInitializeSecurity(
-         NULL,                           //  Allow *all* VSS writers to communicate back!
-         -1,                             //  Default COM authentication service
-         NULL,                           //  Default COM authorization service
-         NULL,                           //  reserved parameter
-         RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
-         RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities 
-         NULL,                           //  Default COM authentication settings
-         EOAC_NONE,                      //  No special options
-         NULL                            //  Reserved parameter
-         );
-
-      if (FAILED(hr))
-         return FALSE;
-   }
-
-   // Release the IVssBackupComponents interface 
-   if (m_pVssObject) {
-      m_pVssObject->Release();
-      m_pVssObject = NULL;
-   }
-
-   // Create the internal backup components object
-   hr = CreateVssBackupComponents(&m_pVssObject);
-   if (FAILED(hr))
-      return FALSE;
-
-   // We are during restore now?
-   m_bDuringRestore = bDuringRestore;
-/*
-   // Call either Initialize for backup or for restore
-   if (m_bDuringRestore)  {
-      hr = m_pVssObject->InitializeForRestore(CComBSTR(L""));
-      if (FAILED(hr))
-         return FALSE;
-   }
-   else
-   {
-      // Initialize for backup
-      hr = m_pVssObject->InitializeForBackup();
-      if (FAILED(hr))
-         return FALSE;
-   }
-
-   
-
-   // Set various properties per backup components instance
-   hr = m_pVssObject->SetBackupState(true, true, VSS_BT_FULL, false);
-   if (FAILED(hr))
-      return FALSE;
-*/
-// Keep the context
-   m_dwContext = dwContext;
-
-   return TRUE;
+    //return Initialize (VSS_CTX_BACKUP);
+   return Initialize (0);
 }
 
 
-void VSSClient::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
-{
-     // Wait until the async operation finishes
-    HRESULT hr = pAsync->Wait();
-
-    // Check the result of the asynchronous operation
-    HRESULT hrReturned = S_OK;
-    hr = pAsync->QueryStatus(&hrReturned, NULL);
 
-    // Check if the async operation succeeded...
-    if(FAILED(hrReturned))
-    {
-        throw(hrReturned);
-    }
-}
 
-BOOL VSSClient::CreateSnapshots(char* szDriveLetters)
-{
-   /* szDriveLetters contains all drive letters in uppercase */
-   /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
-   /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
-   
-   if (!m_pVssObject || m_bBackupIsInitialized)
-      return FALSE;      
-
-   m_bBackupIsInitialized = true;
-
-   // 1. InitializeForBackup
-   HRESULT hr = m_pVssObject->InitializeForBackup();
-   if (FAILED(hr))
-      return FALSE;
-   
-   // 2. SetBackupState
-   hr = m_pVssObject->SetBackupState(true, true, VSS_BT_FULL, false);
-   if (FAILED(hr))
-      return FALSE;
-
-   CComPtr<IVssAsync>  pAsync;
-   VSS_ID latestSnapshotSetID;
-   VSS_ID pid;
-
-   // 3. startSnapshotSet
-
-   m_pVssObject->StartSnapshotSet(&latestSnapshotSetID);
-
-   // 4. AddToSnapshotSet
-
-   WCHAR szDrive[3];
-   szDrive[1] = ':';
-   szDrive[2] = 0;
-
-   wstring volume;
-
-   for (size_t i=0; i < strlen (szDriveLetters); i++) {
-      szDrive[0] = szDriveLetters[i];
-      volume = GetUniqueVolumeNameForPath(szDrive);
-      // store uniquevolumname
-      if (SUCCEEDED(m_pVssObject->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid)))
-         wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
-      else
-         szDriveLetters[i] = tolower (szDriveLetters[i]);               
-   }
-
-   // 5. PrepareForBackup
-
-   m_pVssObject->PrepareForBackup(&pAsync);
-
-   // Waits for the async operation to finish and checks the result
-   WaitAndCheckForAsyncOperation(pAsync);
-
-
-   // 6. DoSnapShotSet
-   
-   pAsync = NULL;
-   m_pVssObject->DoSnapshotSet(&pAsync);
-
-   // Waits for the async operation to finish and checks the result
-   WaitAndCheckForAsyncOperation(pAsync); 
-   
-   /* query snapshot info */
-   QuerySnapshotSet(latestSnapshotSetID);
-
-   return TRUE;
-}
-
-BOOL VSSClient::CloseBackup()
+BOOL VSSClient::GetShadowPath (const char* szFilePath, char* szShadowPath, int nBuflen)
 {
-   if (!m_pVssObject || !m_bBackupIsInitialized)
+   if (!m_bBackupIsInitialized)
       return FALSE;
 
-   m_bBackupIsInitialized = false;
-
-   CComPtr<IVssAsync>  pAsync;
-
-   if (SUCCEEDED(m_pVssObject->BackupComplete(&pAsync))) {
-     // Waits for the async operation to finish and checks the result
-     WaitAndCheckForAsyncOperation(pAsync);
-   }
-   else return FALSE;
-   
-   return TRUE;
-}
-
-// Query all the shadow copies in the given set
-void VSSClient::QuerySnapshotSet(GUID snapshotSetID)
-{   
-   memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
-   
-   if (snapshotSetID == GUID_NULL || m_pVssObject == NULL)
-      return;
-            
-   // Get list all shadow copies. 
-   CComPtr<IVssEnumObject> pIEnumSnapshots;
-   HRESULT hr = m_pVssObject->Query( GUID_NULL, 
-         VSS_OBJECT_NONE, 
-         VSS_OBJECT_SNAPSHOT, 
-         &pIEnumSnapshots );    
-
-   // If there are no shadow copies, just return
-   if (hr == S_FALSE) {
-      return;
-   } 
-
-   // Enumerate all shadow copies. 
-   VSS_OBJECT_PROP Prop;
-   VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
-   
-   while(true)
-   {
-      // Get the next element
-      ULONG ulFetched;
-      hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched );
-
-      // We reached the end of list
-      if (ulFetched == 0)
-         break;
-
-      // Print the shadow copy (if not filtered out)
-      if (Snap.m_SnapshotSetId == snapshotSetID)  {
-         for (char ch='A'-'A';ch<='Z'-'A';ch++) {
-            if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {               
-               WideCharToMultiByte(CP_UTF8,0,Snap.m_pwszSnapshotDeviceObject,-1,m_szShadowCopyName[ch],MAX_PATH*2,NULL,NULL);               
-               break;
-            }
-         }
-      }
-      ::VssFreeSnapshotProperties(&Snap);
-   }
-}
-
-BOOL VSSClient::GetShadowPath (const char* szFilePath, char* szShadowPath, int nBuflen)
-{
    /* check for valid pathname */
    BOOL bIsValidName;
    
index 9b26f0470bbde2515fe99140d74ab9ea2858b34c..d8abd82479280acd2d3c5f9cda969e5d7570265f 100644 (file)
@@ -34,7 +34,6 @@
 #define __VSS_H_
 
 // some forward declarations
-class IVssBackupComponents;
 struct IVssAsync;
 
 class VSSClient
@@ -45,33 +44,60 @@ public:
 
     // Backup Process
     BOOL InitializeForBackup();
-    BOOL CreateSnapshots(char* szDriveLetters);
+    virtual BOOL CreateSnapshots(char* szDriveLetters) = 0;
+    virtual BOOL CloseBackup() = 0;
+    virtual const char* GetDriverName() = 0;
     BOOL GetShadowPath (const char* szFilePath, char* szShadowPath, int nBuflen);
-    BOOL CloseBackup();
-    
-private:
-
-   BOOL Initialize(DWORD dwContext, bool bDuringRestore = false);
-   void WaitAndCheckForAsyncOperation(IVssAsync*  pAsync);
-   void QuerySnapshotSet(GUID snapshotSetID);
 
+         
 private:
+    virtual BOOL Initialize(DWORD dwContext, BOOL bDuringRestore = FALSE) = 0;
+    virtual void WaitAndCheckForAsyncOperation(IVssAsync*  pAsync) = 0;
+    virtual void QuerySnapshotSet(GUID snapshotSetID) = 0;
+
+protected:
+    HMODULE                         m_hLib;
 
-    bool                            m_bCoInitializeCalled;
+    BOOL                            m_bCoInitializeCalled;
     DWORD                           m_dwContext;
 
-    IVssBackupComponents*           m_pVssObject;
+    IUnknown*                       m_pVssObject;
     // TRUE if we are during restore
-    bool                            m_bDuringRestore;
-    bool                            m_bBackupIsInitialized;
+    BOOL                            m_bDuringRestore;
+    BOOL                            m_bBackupIsInitialized;
 
     // drive A will be stored on position 0,Z on pos. 25
     WCHAR                           m_wszUniqueVolumeName[26][MAX_PATH]; // approx. 7 KB
     char /* in utf-8 */             m_szShadowCopyName[26][MAX_PATH*2]; // approx. 7 KB
 };
 
-// define global VssClient
-extern VSSClient g_VSSClient;
+class VSSClientXP:public VSSClient
+{
+public:
+   VSSClientXP();
+   virtual ~VSSClientXP();
+   virtual BOOL CreateSnapshots(char* szDriveLetters);
+   virtual BOOL CloseBackup();
+   virtual const char* GetDriverName() { return "VSS WinXP"; };
+private:
+   virtual BOOL Initialize(DWORD dwContext, BOOL bDuringRestore);
+   virtual void WaitAndCheckForAsyncOperation(IVssAsync* pAsync);
+   virtual void QuerySnapshotSet(GUID snapshotSetID);
+};
+
+class VSSClient2003:public VSSClient
+{
+public:
+   VSSClient2003();
+   virtual ~VSSClient2003();
+   virtual BOOL CreateSnapshots(char* szDriveLetters);
+   virtual BOOL CloseBackup();   
+   virtual const char* GetDriverName() { return "VSS Win 2003"; };
+private:
+   virtual BOOL Initialize(DWORD dwContext, BOOL bDuringRestore);
+   virtual void WaitAndCheckForAsyncOperation(IVssAsync*  pAsync);
+   virtual void QuerySnapshotSet(GUID snapshotSetID);
+};
 
 
 #endif /* __VSS_H_ */
diff --git a/bacula/src/win32/compat/vss_W2K3.cpp b/bacula/src/win32/compat/vss_W2K3.cpp
new file mode 100644 (file)
index 0000000..d052f79
--- /dev/null
@@ -0,0 +1,14 @@
+/* 
+we need one class per OS as Microsofts API 
+differs only by calling convention and some
+function we don't use.
+
+vss_generic will handle all versions and
+switch between different headers to include.
+*/
+
+#define B_VSS_W2K3
+#include "vss_generic.cpp"
+
+
+
diff --git a/bacula/src/win32/compat/vss_XP.cpp b/bacula/src/win32/compat/vss_XP.cpp
new file mode 100644 (file)
index 0000000..45c084b
--- /dev/null
@@ -0,0 +1,3 @@
+#define B_VSS_XP
+#include "vss_generic.cpp"
+
diff --git a/bacula/src/win32/compat/vss_generic.cpp b/bacula/src/win32/compat/vss_generic.cpp
new file mode 100644 (file)
index 0000000..81323e4
--- /dev/null
@@ -0,0 +1,410 @@
+//                              -*- Mode: C++ -*-
+// vss.cpp -- Interface to Volume Shadow Copies (VSS)
+//
+// Copyright transferred from MATRIX-Computer GmbH to
+//   Kern Sibbald by express permission.
+//
+// Copyright (C) 2004-2005 Kern Sibbald
+//
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the GNU General Public License as
+//   published by the Free Software Foundation; either version 2 of
+//   the License, or (at your option) any later version.
+//
+//   This program is distributed in the hope that it will be useful,
+//   but WITHOUT ANY WARRANTY; without even the implied warranty of
+//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+//   General Public License for more details.
+//
+//   You should have received a copy of the GNU General Public
+//   License along with this program; if not, write to the Free
+//   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+//   MA 02111-1307, USA.
+//
+// Author          : Thorsten Engel
+// Created On      : Fri May 06 21:44:00 2006
+
+
+#include <stdio.h>
+#include <basetsd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <process.h>
+#include <direct.h>
+#include <winsock2.h>
+#include <windows.h>
+#include <wincon.h>
+#include <winbase.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <conio.h>
+#include <process.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <malloc.h>
+#include <setjmp.h>
+#include <direct.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <io.h>
+
+
+// STL includes
+#include <vector>
+#include <algorithm>
+#include <string>
+#include <fstream>
+using namespace std;   
+
+#include <atlcomcli.h>
+#include <objbase.h>
+
+// Used for safe string manipulation
+#include <strsafe.h>
+
+#ifdef B_VSS_XP
+   #pragma message("compile VSS for Windows XP")   
+   #define VSSClientGeneric VSSClientXP
+
+   #include "vss/inc/WinXP/vss.h"
+   #include "vss/inc/WinXP/vswriter.h"
+   #include "vss/inc/WinXP/vsbackup.h"
+   
+   /* In VSSAPI.DLL */
+   typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **);
+   typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
+   
+   static t_CreateVssBackupComponents p_CreateVssBackupComponents = NULL;
+   static t_VssFreeSnapshotProperties p_VssFreeSnapshotProperties = NULL;
+
+   #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
+#endif
+
+#ifdef B_VSS_W2K3
+   #pragma message("compile VSS for Windows 2003")
+   #define VSSClientGeneric VSSClient2003
+
+   #include "vss/inc/Win2003/vss.h"
+   #include "vss/inc/Win2003/vswriter.h"
+   #include "vss/inc/Win2003/vsbackup.h"
+   
+   /* In VSSAPI.DLL */
+   typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **);
+   typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
+   
+   static t_CreateVssBackupComponents p_CreateVssBackupComponents = NULL;
+   static t_VssFreeSnapshotProperties p_VssFreeSnapshotProperties = NULL;
+
+   #define VSSVBACK_ENTRY "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
+#endif
+
+#include "vss.h"
+
+/*  
+ *
+ * some helper functions 
+ *
+ *
+ */
+
+// Append a backslash to the current string 
+inline wstring AppendBackslash(wstring str)
+{
+    if (str.length() == 0)
+        return wstring(L"\\");
+    if (str[str.length() - 1] == L'\\')
+        return str;
+    return str.append(L"\\");
+}
+
+// Get the unique volume name for the given path
+inline wstring GetUniqueVolumeNameForPath(wstring path)
+{
+    _ASSERTE(path.length() > 0);
+
+    // Add the backslash termination, if needed
+    path = AppendBackslash(path);
+
+    // Get the root path of the volume
+    WCHAR volumeRootPath[MAX_PATH];
+    WCHAR volumeName[MAX_PATH];
+    WCHAR volumeUniqueName[MAX_PATH];
+
+    if (!GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH))
+      return L'\0';
+    
+    // Get the volume name alias (might be different from the unique volume name in rare cases)
+    if (!GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH))
+       return L'\0';
+    
+    // Get the unique volume name    
+    if (!GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH))
+       return L'\0';
+    
+    return volumeUniqueName;
+}
+
+
+
+// Constructor
+
+VSSClientGeneric::VSSClientGeneric()
+{
+   m_hLib = LoadLibraryA("VSSAPI.DLL");
+   if (m_hLib) {      
+      p_CreateVssBackupComponents = (t_CreateVssBackupComponents)
+         GetProcAddress(m_hLib, VSSVBACK_ENTRY);
+                                 
+      p_VssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
+          GetProcAddress(m_hLib, "VssFreeSnapshotProperties");      
+   } 
+}
+
+
+
+// Destructor
+VSSClientGeneric::~VSSClientGeneric()
+{
+   if (m_hLib)
+      FreeLibrary(m_hLib);
+}
+
+// Initialize the COM infrastructure and the internal pointers
+BOOL VSSClientGeneric::Initialize(DWORD dwContext, BOOL bDuringRestore)
+{
+   if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties))
+      return FALSE;
+
+   HRESULT hr;
+   // Initialize COM 
+   if (!m_bCoInitializeCalled)  {
+      if (FAILED(CoInitialize(NULL)))
+         return FALSE;
+
+      m_bCoInitializeCalled = true;
+
+      // Initialize COM security
+      hr =
+         CoInitializeSecurity(
+         NULL,                           //  Allow *all* VSS writers to communicate back!
+         -1,                             //  Default COM authentication service
+         NULL,                           //  Default COM authorization service
+         NULL,                           //  reserved parameter
+         RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  //  Strongest COM authentication level
+         RPC_C_IMP_LEVEL_IDENTIFY,       //  Minimal impersonation abilities 
+         NULL,                           //  Default COM authentication settings
+         EOAC_NONE,                      //  No special options
+         NULL                            //  Reserved parameter
+         );
+
+      if (FAILED(hr))
+         return FALSE;
+   }
+
+   IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
+   // Release the IVssBackupComponents interface 
+   if (pVss) {
+      pVss->Release();
+      pVss = NULL;
+   }
+
+   // Create the internal backup components object
+   hr = p_CreateVssBackupComponents((IVssBackupComponents**) &m_pVssObject);
+   if (FAILED(hr))
+      return FALSE;
+
+   // We are during restore now?
+   m_bDuringRestore = bDuringRestore;
+
+   // Keep the context
+   m_dwContext = dwContext;
+
+   return TRUE;
+}
+
+
+void VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync)
+{
+     // Wait until the async operation finishes
+    HRESULT hr = pAsync->Wait();
+
+    // Check the result of the asynchronous operation
+    HRESULT hrReturned = S_OK;
+    hr = pAsync->QueryStatus(&hrReturned, NULL);
+
+    // Check if the async operation succeeded...
+    if(FAILED(hrReturned))
+    {
+      PWCHAR pwszBuffer = NULL;
+      DWORD dwRet = ::FormatMessageW(
+         FORMAT_MESSAGE_ALLOCATE_BUFFER 
+         | FORMAT_MESSAGE_FROM_SYSTEM 
+         | FORMAT_MESSAGE_IGNORE_INSERTS,
+         NULL, hrReturned, 
+         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
+         (LPWSTR)&pwszBuffer, 0, NULL);
+
+      // No message found for this error. Just return <Unknown>
+      if (dwRet != 0)
+      {
+         // Convert the message into wstring         
+    
+         LocalFree(pwszBuffer);         
+      }
+      throw(hrReturned);
+    }
+}
+
+BOOL VSSClientGeneric::CreateSnapshots(char* szDriveLetters)
+{
+   /* szDriveLetters contains all drive letters in uppercase */
+   /* if a drive can not being added, it's converted to lowercase in szDriveLetters */
+   /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vss/base/ivssbackupcomponents_startsnapshotset.asp */
+   
+   if (!m_pVssObject || m_bBackupIsInitialized)
+      return FALSE;      
+
+   IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
+
+   // 1. InitializeForBackup
+   HRESULT hr = pVss->InitializeForBackup();
+   if (FAILED(hr))
+      return FALSE;
+   
+   // 2. SetBackupState
+   hr = pVss->SetBackupState(true, true, VSS_BT_FULL, false);
+   if (FAILED(hr))
+      return FALSE;
+
+   CComPtr<IVssAsync>  pAsync1;
+   CComPtr<IVssAsync>  pAsync2;
+   VSS_ID latestSnapshotSetID;
+   VSS_ID pid;
+
+   // 3. startSnapshotSet
+
+   pVss->StartSnapshotSet(&latestSnapshotSetID);
+
+   // 4. AddToSnapshotSet
+
+   WCHAR szDrive[3];
+   szDrive[1] = ':';
+   szDrive[2] = 0;
+
+   wstring volume;
+
+   for (size_t i=0; i < strlen (szDriveLetters); i++) {
+      szDrive[0] = szDriveLetters[i];
+      volume = GetUniqueVolumeNameForPath(szDrive);
+      // store uniquevolumname
+      if (SUCCEEDED(pVss->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid)))
+         wcsncpy (m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR) volume.c_str(), MAX_PATH);
+      else
+         szDriveLetters[i] = tolower (szDriveLetters[i]);               
+   }
+
+   // 5. PrepareForBackup
+
+   pVss->PrepareForBackup(&pAsync1);
+
+   // Waits for the async operation to finish and checks the result
+   WaitAndCheckForAsyncOperation(pAsync1);
+
+
+   // 6. DoSnapShotSet
+
+   pVss->DoSnapshotSet(&pAsync2);
+
+   // Waits for the async operation to finish and checks the result
+   WaitAndCheckForAsyncOperation(pAsync2); 
+   
+   /* query snapshot info */
+   QuerySnapshotSet(latestSnapshotSetID);
+
+   m_bBackupIsInitialized = true;
+
+   return TRUE;
+}
+
+BOOL VSSClientGeneric::CloseBackup()
+{
+   if (!m_pVssObject)
+      return FALSE;
+
+   BOOL bRet = FALSE;
+   IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
+   CComPtr<IVssAsync>  pAsync;
+   
+   m_bBackupIsInitialized = false;
+
+   if (SUCCEEDED(pVss->BackupComplete(&pAsync))) {
+     // Waits for the async operation to finish and checks the result
+     WaitAndCheckForAsyncOperation(pAsync);
+     bRet = TRUE;     
+   }
+   else {
+      pVss->AbortBackup();
+   }
+
+   pVss->Release();
+   m_pVssObject = NULL;
+
+   return bRet;
+}
+
+// Query all the shadow copies in the given set
+void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID)
+{   
+   if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties))
+      return;
+
+   memset (m_szShadowCopyName,0,sizeof (m_szShadowCopyName));
+   
+   if (snapshotSetID == GUID_NULL || m_pVssObject == NULL)
+      return;
+
+   IVssBackupComponents* pVss = (IVssBackupComponents*) m_pVssObject;
+
+            
+   // Get list all shadow copies. 
+   CComPtr<IVssEnumObject> pIEnumSnapshots;
+   HRESULT hr = pVss->Query( GUID_NULL, 
+         VSS_OBJECT_NONE, 
+         VSS_OBJECT_SNAPSHOT, 
+         &pIEnumSnapshots );    
+
+   // If there are no shadow copies, just return
+   if (hr == S_FALSE) {
+      return;
+   } 
+
+   // Enumerate all shadow copies. 
+   VSS_OBJECT_PROP Prop;
+   VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
+   
+   while(true)
+   {
+      // Get the next element
+      ULONG ulFetched;
+      hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched );
+
+      // We reached the end of list
+      if (ulFetched == 0)
+         break;
+
+      // Print the shadow copy (if not filtered out)
+      if (Snap.m_SnapshotSetId == snapshotSetID)  {
+         for (char ch='A'-'A';ch<='Z'-'A';ch++) {
+            if (wcscmp(Snap.m_pwszOriginalVolumeName, m_wszUniqueVolumeName[ch]) == 0) {               
+               WideCharToMultiByte(CP_UTF8,0,Snap.m_pwszSnapshotDeviceObject,-1,m_szShadowCopyName[ch],MAX_PATH*2,NULL,NULL);               
+               break;
+            }
+         }
+      }
+      p_VssFreeSnapshotProperties(&Snap);
+   }
+}
+