From 02562b08124c6a4107d3cfb7eaf8b9746f4c7195 Mon Sep 17 00:00:00 2001 From: Oleg Livshyts Date: Wed, 3 Dec 2014 10:32:50 +0100 Subject: [PATCH] Automatic detection of volume mount points Volume Mount Points are now automatically detected. If we detect a volume mount point, we check wich volume is mounted there and add it to the snapshotset. If the volume is not already in the snapshotset, we add it and check if the new volume also has volume mount points. The total number of volume mount points and the number of volume mount point volumes added to the snapshotset is reported, if VMPs exist. Signed-off-by: Philipp Storz Signed-off-by: Marco van Wieringen --- src/filed/dir_cmd.c | 27 +++--- src/findlib/find_one.c | 12 ++- src/findlib/protos.h | 1 + src/win32/filed/vss_generic.c | 162 ++++++++++++++++++++++++++++------ src/win32/findlib/win32.c | 24 +++++ src/win32/include/vss.h | 51 ++++++----- 6 files changed, 215 insertions(+), 62 deletions(-) diff --git a/src/filed/dir_cmd.c b/src/filed/dir_cmd.c index ae6d4e20f15..fb116a05c06 100644 --- a/src/filed/dir_cmd.c +++ b/src/filed/dir_cmd.c @@ -1586,9 +1586,9 @@ static bool backup_cmd(JCR *jcr) */ if (jcr->VSS) { if (g_pVSSClient->InitializeForBackup(jcr)) { - int drive_count, vmp_count; + int drive_count; char szWinDriveLetters[27]; - dlist *szWinVolumeMountpoints = NULL; + bool onefs_disabled; generate_plugin_event(jcr, bEventVssBackupAddComponents); @@ -1603,15 +1603,22 @@ static bool backup_cmd(JCR *jcr) generate_plugin_event(jcr, bEventVssPrepareSnapshot, szWinDriveLetters); drive_count = get_win32_driveletters(jcr->ff->fileset, szWinDriveLetters); - vmp_count = get_win32_virtualmountpoints(jcr->ff->fileset, &szWinVolumeMountpoints); - if (drive_count > 0 || vmp_count > 0) { - Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\", Drive(s)=\"%s\" VMP(s)=%d\n"), - g_pVSSClient->GetDriverName(), (drive_count) ? szWinDriveLetters : "None", vmp_count); - if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters, szWinVolumeMountpoints)) { + onefs_disabled = win32_onefs_is_disabled(jcr->ff->fileset); + + if (drive_count > 0) { + Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\", Drive(s)=\"%s\"\n"), + g_pVSSClient->GetDriverName(), (drive_count) ? szWinDriveLetters : "None"); + + if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters, onefs_disabled)) { berrno be; Jmsg(jcr, M_FATAL, 0, _("CreateSGenerate VSS snapshots failed. ERR=%s\n"), be.bstrerror()); } else { + /* + * Inform about VMPs if we have them + */ + g_pVSSClient->ShowVolumeMountPointStats(jcr); + /* * Tell user if snapshot creation of a specific drive failed */ @@ -1631,12 +1638,8 @@ static bool backup_cmd(JCR *jcr) } } - if (szWinVolumeMountpoints) { - szWinVolumeMountpoints->destroy(); - free(szWinVolumeMountpoints); - } } else { - Jmsg(jcr, M_FATAL, 0, _("No drive letters or Volume Mount Points found for generating VSS snapshots.\n")); + Jmsg(jcr, M_FATAL, 0, _("No drive letters found for generating VSS snapshots.\n")); } } else { berrno be; diff --git a/src/findlib/find_one.c b/src/findlib/find_one.c index b1408a3fa7d..1e4c92a944e 100644 --- a/src/findlib/find_one.c +++ b/src/findlib/find_one.c @@ -589,10 +589,18 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, ff_pkt->type = FT_DIRBEGIN; } + bool is_win32_mount_point = false; #if defined(HAVE_WIN32) + is_win32_mount_point = ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT; + if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_REPARSE_POINT) { ff_pkt->type = FT_REPARSE; } + + /* treat win32 mount points (Volume Mount Points) as directories */ + if (is_win32_mount_point) { + ff_pkt->type = FT_DIRBEGIN; + } #endif /* @@ -637,10 +645,6 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, * to cross, or we may be restricted by a list of permitted * file systems. */ - bool is_win32_mount_point = false; -#if defined(HAVE_WIN32) - is_win32_mount_point = ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT; -#endif if (!top_level && bit_is_set(FO_NO_RECURSION, ff_pkt->flags)) { ff_pkt->type = FT_NORECURSE; recurse = false; diff --git a/src/findlib/protos.h b/src/findlib/protos.h index 3d37ef7e0bf..b7226397db5 100644 --- a/src/findlib/protos.h +++ b/src/findlib/protos.h @@ -107,6 +107,7 @@ void check_include_list_shadowing(JCR *jcr, findFILESET *fileset); #if defined(HAVE_WIN32) /* win32.c */ +bool win32_onefs_is_disabled(findFILESET *fileset); int get_win32_driveletters(findFILESET *fileset, char *szDrives); int get_win32_virtualmountpoints(findFILESET *fileset, dlist **szVmps); bool expand_win32_fileset(findFILESET *fileset); diff --git a/src/win32/filed/vss_generic.c b/src/win32/filed/vss_generic.c index 3c7e70bd95d..c79709a0ccd 100644 --- a/src/win32/filed/vss_generic.c +++ b/src/win32/filed/vss_generic.c @@ -105,6 +105,9 @@ class IXMLDOMDocument; #include "Win2003/vsbackup.h" #endif +#define VSS_ERROR_OBJECT_ALREADY_EXISTS 0x8004230D + + /* In VSSAPI.DLL */ typedef HRESULT (STDAPICALLTYPE* t_CreateVssBackupComponents)(OUT IVssBackupComponents **); typedef void (APIENTRY* t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*); @@ -306,6 +309,68 @@ static inline wstring GetUniqueVolumeNameForPath(wstring path) return volumeUniqueName; } +static inline POOLMEM *GetMountedVolumeForMountPointPath(POOLMEM *volumepath, POOLMEM *mountpoint) +{ + POOLMEM *fullPath, *buf, *vol; + int len; + + /* + * GetUniqueVolumeNameForPath() should be used here + */ + len = strlen(volumepath) + 1; + fullPath = get_pool_memory(PM_FNAME); + pm_strcpy(fullPath, volumepath); + pm_strcat(fullPath, mountpoint); + + buf = get_pool_memory(PM_FNAME); + GetVolumeNameForVolumeMountPoint(fullPath, buf, len); + + Dmsg3(200, "%s%s mounts volume %s\n", volumepath, mountpoint, buf); + + vol = get_pool_memory(PM_FNAME); + UTF8_2_wchar(&vol, buf); + + free_pool_memory(fullPath); + free_pool_memory(buf); + + return vol; +} + +static inline bool HandleVolumeMountPoint(VSSClientGeneric *pVssClient, + IVssBackupComponents *pVssObj, + POOLMEM *volumepath, + POOLMEM *mountpoint) +{ + bool retval = false; + HRESULT hr; + POOLMEM *vol = NULL; + POOLMEM *pvol; + VSS_ID pid; + + vol = GetMountedVolumeForMountPointPath(volumepath, mountpoint); + hr = pVssObj->AddToSnapshotSet((LPWSTR)vol, GUID_NULL, &pid); + + pvol = get_pool_memory(PM_FNAME); + wchar_2_UTF8(&pvol, (wchar_t *)vol); + + if (SUCCEEDED(hr)) { + pVssClient->AddVolumeMountPointSnapshots(pVssObj, (wchar_t *)vol); + Dmsg1(200, "%s added to snapshotset \n", pvol); + retval = true; + } else if((unsigned)hr == VSS_ERROR_OBJECT_ALREADY_EXISTS) { + Dmsg1(200, "%s already in snapshotset, skipping.\n" ,pvol); + } else { + Dmsg3(200, "%s with vmp %s could not be added to snapshotset, COM ERROR: 0x%X\n", vol, mountpoint, hr); + } + + free_pool_memory(pvol); + if (vol) { + free_pool_memory(vol); + } + + return retval; +} + /* * Helper macro for quick treatment of case statements for error codes */ @@ -375,10 +440,12 @@ VSSClientGeneric::~VSSClientGeneric() */ bool VSSClientGeneric::Initialize(DWORD dwContext, bool bDuringRestore) { + VMPs = 0; + VMP_snapshots = 0; HRESULT hr; CComPtr pAsync1; VSS_BACKUP_TYPE backup_type; - IVssBackupComponents* pVssObj = (IVssBackupComponents*)m_pVssObject; + IVssBackupComponents *pVssObj = (IVssBackupComponents *)m_pVssObject; if (!(p_CreateVssBackupComponents && p_VssFreeSnapshotProperties)) { Dmsg2(0, "VSSClientGeneric::Initialize: p_CreateVssBackupComponents=0x%08X, p_VssFreeSnapshotProperties=0x%08X\n", p_CreateVssBackupComponents, p_VssFreeSnapshotProperties); @@ -447,7 +514,7 @@ bool VSSClientGeneric::Initialize(DWORD dwContext, bool bDuringRestore) /* * Define shorthand VssObject with time */ - pVssObj = (IVssBackupComponents*)m_pVssObject; + pVssObj = (IVssBackupComponents *)m_pVssObject; if (!bDuringRestore) { @@ -580,7 +647,7 @@ bool VSSClientGeneric::WaitAndCheckForAsyncOperation(IVssAsync* pAsync) /* * Add all drive letters that need to be snapshotted. */ -void VSSClientGeneric::AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters) +void VSSClientGeneric::AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters, bool onefs_disabled) { wstring volume; wchar_t szDrive[3]; @@ -600,43 +667,89 @@ void VSSClientGeneric::AddDriveSnapshots(IVssBackupComponents *pVssObj, char *sz /* * Store uniquevolumname. */ + if (SUCCEEDED(pVssObj->AddToSnapshotSet((LPWSTR)volume.c_str(), GUID_NULL, &pid))) { if (debug_level >= 200) { + POOLMEM *szBuf = get_pool_memory(PM_FNAME); wchar_2_UTF8(&szBuf, volume.c_str()); - Dmsg1(200, "VSSClientGeneric::AddDriveSnapshots added snapshot for drive with volumename %s\n", szBuf); - + Dmsg2(200, "%s added to snapshotset (Drive %s:\\)\n", szBuf, szDrive); free_pool_memory(szBuf); } wcsncpy(m_wszUniqueVolumeName[szDriveLetters[i]-'A'], (LPWSTR)volume.c_str(), MAX_PATH); } else { szDriveLetters[i] = tolower(szDriveLetters[i]); } + if (onefs_disabled) { + AddVolumeMountPointSnapshots(pVssObj, (LPWSTR)volume.c_str()); + } else { + Jmsg(m_jcr, M_INFO, 0, "VolumeMountpoints are not processed as onefs = yes.\n"); + } } } /* * Add all volume mountpoints that need to be snapshotted. + * Volumes can be mounted multiple times, but can only be added to the snapshotset once. + * So we skip adding a volume if it is already in snapshotset. + * We count the total number of vmps and the number of volumes we added to the snapshotset. */ -void VSSClientGeneric::AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, dlist *szVmps) +void VSSClientGeneric::AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, LPWSTR volume) { - dlistString *vmp; - POOLMEM *volume; - VSS_ID pid; + BOOL b; + int len; + HANDLE hMount; + POOLMEM *mp, *path; - if (szVmps) { - volume = get_pool_memory(PM_FNAME); - foreach_dlist(vmp, szVmps) { - Dmsg1(200, "VSSClientGeneric::AddVolumeMountPointSnapshots added snapshot for volume mountpoint with volumename %s\n", vmp->c_str()); - UTF8_2_wchar(&volume, vmp->c_str()); - pVssObj->AddToSnapshotSet((LPWSTR)volume, GUID_NULL, &pid); + mp = get_pool_memory(PM_FNAME); + path = get_pool_memory(PM_FNAME); + + wchar_2_UTF8(&path, volume); + + len = wcslen(volume) + 1; + + hMount = FindFirstVolumeMountPoint(path, mp, len); + if (hMount != INVALID_HANDLE_VALUE) { + /* + * Count number of vmps. + */ + VMPs += 1; + if (HandleVolumeMountPoint(this, pVssObj, path, mp)) { + /* + * Count vmps that were snapshotted + */ + VMP_snapshots += 1; + } + + while ((b = FindNextVolumeMountPoint(hMount, mp, len))) { + /* + * Count number of vmps. + */ + VMPs += 1; + if (HandleVolumeMountPoint(this, pVssObj, path, mp)) { + /* + * Count vmps that were snapshotted + */ + VMP_snapshots += 1; + } } - free_pool_memory(volume); } + + FindVolumeMountPointClose(hMount); + + free_pool_memory(path); + free_pool_memory(mp); } -bool VSSClientGeneric::CreateSnapshots(char *szDriveLetters, dlist *szVmps) +void VSSClientGeneric::ShowVolumeMountPointStats(JCR *jcr) +{ + if (VMPs) { + Jmsg(jcr, M_INFO, 0, _("Volume Mount Points found: %d, added to snapshotset: %d\n"), VMPs, VMP_snapshots); + } +} + +bool VSSClientGeneric::CreateSnapshots(char *szDriveLetters, bool onefs_disabled) { IVssBackupComponents *pVssObj; CComPtr pAsync1; @@ -670,10 +783,7 @@ bool VSSClientGeneric::CreateSnapshots(char *szDriveLetters, dlist *szVmps) * AddToSnapshotSet */ if (szDriveLetters) { - AddDriveSnapshots(pVssObj, szDriveLetters); - } - if (szVmps) { - AddVolumeMountPointSnapshots(pVssObj, szVmps); + AddDriveSnapshots(pVssObj, szDriveLetters, onefs_disabled); } /* @@ -860,10 +970,10 @@ void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID) * Get list all shadow copies. */ CComPtr pIEnumSnapshots; - HRESULT hr = pVssObj->Query( GUID_NULL, - VSS_OBJECT_NONE, - VSS_OBJECT_SNAPSHOT, - (IVssEnumObject**)(&pIEnumSnapshots) ); + HRESULT hr = pVssObj->Query(GUID_NULL, + VSS_OBJECT_NONE, + VSS_OBJECT_SNAPSHOT, + (IVssEnumObject**)(&pIEnumSnapshots)); /* * If there are no shadow copies, just return @@ -885,7 +995,7 @@ void VSSClientGeneric::QuerySnapshotSet(GUID snapshotSetID) * Get the next element */ ULONG ulFetched; - hr = (pIEnumSnapshots.p)->Next( 1, &Prop, &ulFetched ); + hr = (pIEnumSnapshots.p)->Next(1, &Prop, &ulFetched); /* * We reached the end of list diff --git a/src/win32/findlib/win32.c b/src/win32/findlib/win32.c index 13e61bdefc2..a4f5f929240 100644 --- a/src/win32/findlib/win32.c +++ b/src/win32/findlib/win32.c @@ -29,6 +29,30 @@ #include "find.h" #include "lib/cbuf.h" +/* + * We need to analyze if a fileset contains onefs=no as option, because only then + * we need to snapshot submounted vmps + */ +bool win32_onefs_is_disabled(findFILESET *fileset) +{ + findINCEXE *incexe; + + for (int i = 0; i < fileset->include_list.size(); i++) { + incexe = (findINCEXE *)fileset->include_list.get(i); + /* + * Look through all files and check + */ + for (int j = 0; j < incexe->opts_list.size(); j++) { + findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + if (bit_is_set(FO_MULTIFS, fo->flags)) { + return true; + } + } + } + + return false; +} + /* * For VSS we need to know which windows drives are used, because we create a snapshot * of all used drives. This function returns the number of used drives and fills diff --git a/src/win32/include/vss.h b/src/win32/include/vss.h index 2f463b2614b..191bfe4aa7d 100644 --- a/src/win32/include/vss.h +++ b/src/win32/include/vss.h @@ -55,9 +55,11 @@ class VSSClient // Backup Process bool InitializeForBackup(JCR *jcr); bool InitializeForRestore(JCR *jcr); - virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters) = 0; - virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, dlist *szVmps) = 0; - virtual bool CreateSnapshots(char *szDriveLetters, dlist *szVmps) = 0; + virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters, bool onefs_disabled) = 0; + virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, LPWSTR volume) = 0; + virtual void ShowVolumeMountPointStats(JCR *jcr) = 0; + + virtual bool CreateSnapshots(char *szDriveLetters, bool onefs_disabled) = 0; virtual bool CloseBackup() = 0; virtual bool CloseRestore() = 0; virtual WCHAR *GetMetadata() = 0; @@ -88,9 +90,12 @@ class VSSClient IUnknown *m_pVssObject; GUID m_uidCurrentSnapshotSet; - // drive A will be stored on position 0,Z on pos. 25 - wchar_t m_wszUniqueVolumeName[26][MAX_PATH]; // approx. 7 KB - wchar_t m_szShadowCopyName[26][MAX_PATH]; // approx. 7 KB + /* + ! drive A will be stored on position 0, Z on pos. 25 + */ + wchar_t m_wszUniqueVolumeName[26][MAX_PATH]; + wchar_t m_szShadowCopyName[26][MAX_PATH]; + wchar_t *m_metadata; alist *m_pAlistWriterState; alist *m_pAlistWriterInfoText; @@ -101,17 +106,19 @@ class VSSClient bool m_bBackupIsInitialized; bool m_bWriterStatusCurrent; - WCHAR *m_metadata; + int VMPs; /* volume mount points */ + int VMP_snapshots; /* volume mount points that are snapshotted */ }; -class VSSClientXP:public VSSClient +class VSSClientXP: public VSSClient { public: VSSClientXP(); virtual ~VSSClientXP(); - virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters); - virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, dlist *szVmps); - virtual bool CreateSnapshots(char *szDriveLetters, dlist *szVmps); + virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters, bool onefs_disabled); + virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, LPWSTR volume); + virtual void ShowVolumeMountPointStats(JCR *jcr); + virtual bool CreateSnapshots(char *szDriveLetters, bool onefs_disabled); virtual bool CloseBackup(); virtual bool CloseRestore(); virtual WCHAR *GetMetadata(); @@ -120,6 +127,7 @@ class VSSClientXP:public VSSClient #else virtual const char *GetDriverName() { return "Win32 VSS"; }; #endif + private: virtual bool Initialize(DWORD dwContext, bool bDuringRestore); virtual bool WaitAndCheckForAsyncOperation(IVssAsync *pAsync); @@ -127,14 +135,15 @@ class VSSClientXP:public VSSClient bool CheckWriterStatus(); }; -class VSSClient2003:public VSSClient +class VSSClient2003: public VSSClient { public: VSSClient2003(); virtual ~VSSClient2003(); - virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters); - virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, dlist *szVmps); - virtual bool CreateSnapshots(char *szDriveLetters, dlist *szVmps); + virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters, bool onefs_disabled); + virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, LPWSTR volume); + virtual void ShowVolumeMountPointStats(JCR *jcr); + virtual bool CreateSnapshots(char *szDriveLetters, bool onefs_disabled); virtual bool CloseBackup(); virtual bool CloseRestore(); virtual WCHAR *GetMetadata(); @@ -143,6 +152,7 @@ class VSSClient2003:public VSSClient #else virtual const char *GetDriverName() { return "Win32 VSS"; }; #endif + private: virtual bool Initialize(DWORD dwContext, bool bDuringRestore); virtual bool WaitAndCheckForAsyncOperation(IVssAsync *pAsync); @@ -150,14 +160,15 @@ class VSSClient2003:public VSSClient bool CheckWriterStatus(); }; -class VSSClientVista:public VSSClient +class VSSClientVista: public VSSClient { public: VSSClientVista(); virtual ~VSSClientVista(); - virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters); - virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, dlist *szVmps); - virtual bool CreateSnapshots(char *szDriveLetters, dlist *szVmps); + virtual void AddDriveSnapshots(IVssBackupComponents *pVssObj, char *szDriveLetters, bool onefs_disabled); + virtual void AddVolumeMountPointSnapshots(IVssBackupComponents *pVssObj, LPWSTR volume); + virtual void ShowVolumeMountPointStats(JCR *jcr); + virtual bool CreateSnapshots(char *szDriveLetters, bool onefs_disabled); virtual bool CloseBackup(); virtual bool CloseRestore(); virtual WCHAR *GetMetadata(); @@ -166,6 +177,7 @@ class VSSClientVista:public VSSClient #else virtual const char *GetDriverName() { return "Win32 VSS"; }; #endif + private: virtual bool Initialize(DWORD dwContext, bool bDuringRestore); virtual bool WaitAndCheckForAsyncOperation(IVssAsync *pAsync); @@ -173,7 +185,6 @@ class VSSClientVista:public VSSClient bool CheckWriterStatus(); }; - extern VSSClient *g_pVSSClient; bool VSSPathConvert(const char *szFilePath, char *szShadowPath, int nBuflen);