From: Norbert Bizet Date: Mon, 12 Oct 2020 10:00:27 +0000 (+0200) Subject: win32: Move the MTab code to compat.cpp and use it with Plugins X-Git-Tag: Release-11.3.2~1004 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=afea039726d4dbd15a9537ea520d85c153080b8c;p=thirdparty%2Fbacula.git win32: Move the MTab code to compat.cpp and use it with Plugins --- diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 1dedbe34b..5b7aaf428 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -2190,18 +2190,21 @@ static void list_drives(findFILESET *fileset, dlist *name_list, MTab *mtab) * */ static int -get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) +get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives, void* mtab_def) { int nCount = 0; - #ifdef HAVE_WIN32 findFILESET *fileset = ff->fileset; findINCEXE *alldrives = NULL, *inc = NULL; uint64_t flags = 0; char drive[4]; - MTab mtab; - mtab.get(); /* read the disk structure */ + /* give it our best shot */ + MTab *mtab = (MTab*) mtab_def; + if (!mtab_def) { + mtab = New(MTab()); + mtab->get(); + } /* We check if we need to complete the fileset with File=/ */ if (fileset) { @@ -2216,7 +2219,7 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) * sub volumes are added in the second part if needed */ alldrives = incexe; /* Keep it if MULTIFS is set */ - list_drives(fileset, &incexe->name_list, &mtab); + list_drives(fileset, &incexe->name_list, mtab); } } } @@ -2226,7 +2229,7 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) strcpy(drive, "c:\\"); for (int i=0; szDrives[i] ; i++) { drive[0] = szDrives[i]; - if (mtab.addInSnapshotSet(drive)) { /* When all volumes are selected, we can stop */ + if (mtab->addInSnapshotSet(drive)) { /* When all volumes are selected, we can stop */ Dmsg0(DT_VOLUME|50, "All Volumes are marked, stopping the loop here\n"); goto all_included; } @@ -2234,6 +2237,11 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) } if (fileset) { + + /* Writer dictates the snapshot included components + * but we still give the chanche to include extra files to + * the snapshot set via fileset include_list + */ dlistString *node; for (int i=0; iinclude_list.size(); i++) { @@ -2242,7 +2250,7 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) /* look through all files */ foreach_dlist(node, &incexe->name_list) { char *fname = node->c_str(); - if (mtab.addInSnapshotSet(fname)) { + if (mtab->addInSnapshotSet(fname)) { /* When all volumes are selected, we can stop */ Dmsg0(DT_VOLUME|50, "All Volumes are marked, stopping the loop here\n"); goto all_included; @@ -2261,7 +2269,7 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) Dmsg0(DT_VOLUME|50, "OneFS is set, looking for remaining volumes\n"); - foreach_rblist(elt, mtab.entries) { + foreach_rblist(elt, mtab->entries) { if (elt->in_SnapshotSet) { continue; /* Already in */ } @@ -2281,15 +2289,17 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) /* First thing is to look in the exclude list to see if this directory * is explicitly excluded */ - if (is_excluded(fileset, fn)) { + if (!is_excluded(fileset, fn)) { + elt->setInSnapshotSet(); + } else { Dmsg1(DT_VOLUME|50, "Looks to be excluded %s\n", fn); continue; } /* c:/vol/vol2/vol3 - * will look c:/, then c:/vol/, then c:/vol2/ and if one of them - * is selected, the sub volume will be directly marked. - */ + * will look c:/, then c:/vol/, then c:/vol2/ and if one of them + * is selected, the sub volume will be directly marked. + */ for (char *p1 = fn ; *p1 && !elt->in_SnapshotSet ; p1++) { if (IsPathSeparator(*p1)) { bool to_add=false; /* Add this volume to the FileSet ? */ @@ -2297,10 +2307,10 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) *(p1 + 1) = 0; /* We look for the previous directory, and if marked, we mark - * the current one as well - */ + * the current one as well + */ Dmsg1(DT_VOLUME|50, "Looking for %s\n", fn); - elt2 = mtab.search(fn); + elt2 = mtab->search(fn); if (elt2 && elt2->in_SnapshotSet) { Dmsg0(DT_VOLUME|50, "Put volume in SnapshotSet\n"); elt->setInSnapshotSet(); @@ -2308,8 +2318,8 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) } /* Find where to add the new volume, normally near the - * root volume, or if we are using / - */ + * root volume, or if we are using / + */ if (alldrives) { inc = alldrives; } else { @@ -2337,10 +2347,10 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) } free_pool_memory(fn); } - all_included: +all_included: /* Now, we look the volume list to know which one to include */ MTabEntry *elt; - foreach_rblist(elt, mtab.entries) { + foreach_rblist(elt, mtab->entries) { if (elt->in_SnapshotSet) { Dmsg1(DT_VOLUME|50,"Adding volume in mount_points list %ls\n",elt->volumeName); nCount++; @@ -2349,6 +2359,9 @@ get_win32_driveletters(JCR *jcr, FF_PKT *ff, char* szDrives) } } + if (!mtab_def) { + delete mtab; + } #endif /* HAVE_WIN32 */ return nCount; } @@ -2366,7 +2379,7 @@ static int estimate_cmd(JCR *jcr) } /* On windows, the fileset can be completed by this function (File=/ => File=C:/ + File=E:/) */ - get_win32_driveletters(jcr, jcr->ff, NULL); + get_win32_driveletters(jcr, jcr->ff, NULL, NULL); make_estimate(jcr); dir->fsend(OKest, edit_uint64_with_commas(jcr->num_files_examined, ed1), @@ -2888,12 +2901,13 @@ static int backup_cmd(JCR *jcr) /* START VSS ON WIN32 */ jcr->pVSSClient = VSSInit(); if (jcr->pVSSClient->InitializeForBackup(jcr)) { - generate_plugin_event(jcr, bEventVssBackupAddComponents); + MTab *tab = jcr->pVSSClient->GetVolumeList(); + generate_plugin_event(jcr, bEventVssBackupAddComponents, tab); /* tell vss which drives to snapshot */ char szWinDriveLetters[27]; *szWinDriveLetters=0; generate_plugin_event(jcr, bEventVssPrepareSnapshot, szWinDriveLetters); - if (get_win32_driveletters(jcr, jcr->ff, szWinDriveLetters)) { + if (get_win32_driveletters(jcr, jcr->ff, szWinDriveLetters, tab)) { Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\"\n"), jcr->pVSSClient->GetDriverName()); @@ -2927,7 +2941,7 @@ static int backup_cmd(JCR *jcr) } else { /* No snapshot */ /* On windows, the FileSet might be adjusted with the options that are used (File=/ => File=C:/, File=E:/) */ - get_win32_driveletters(jcr, jcr->ff, NULL); + get_win32_driveletters(jcr, jcr->ff, NULL, NULL); } /* Call RunScript just after the Snapshot creation, usually, we restart services */ run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS"); diff --git a/bacula/src/lib/rblist.h b/bacula/src/lib/rblist.h index 6e19cd709..becd7377d 100644 --- a/bacula/src/lib/rblist.h +++ b/bacula/src/lib/rblist.h @@ -24,6 +24,9 @@ * */ +#ifndef RBLIST_H +#define RBLIST_H + #define M_ABORT 1 /* @@ -154,3 +157,4 @@ inline bool rblist::red(const void *item) const { return ((rblink *)(((char *)item)+loffset))->red; } +#endif /* RBLIST_H */ diff --git a/bacula/src/win32/compat/mtab.cpp b/bacula/src/win32/compat/mtab.cpp new file mode 100644 index 000000000..69e1820a7 --- /dev/null +++ b/bacula/src/win32/compat/mtab.cpp @@ -0,0 +1,295 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2020 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ + +#include "bacula.h" +#include "compat.h" +#include "mtab.h" +#include "ms_atl.h" +#include +#undef setlocale +#include +using namespace std; + +#define dbglvl_snap DT_VOLUME|50 + +/* empty namespace to avoid collisions */ +namespace { + // Append a backslash to the current string + 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 + wstring GetUniqueVolumeNameForPath(wstring path, wstring &rootPath) + { + if (path.length() <= 0) { + //Dmsg0(50, "Failed path.len <= 0\n"); + return L""; + } + + // Add the backslash termination, if needed + path = AppendBackslash(path); + //Dmsg1(50, "Path=%ls\n", path.c_str()); + + // Get the root path of the volume + wchar_t volumeRootPath[MAX_PATH]; + wchar_t volumeName[MAX_PATH]; + wchar_t volumeUniqueName[MAX_PATH]; + + volumeRootPath[0] = 0; + volumeName[0] = 0; + volumeUniqueName[0] = 0; + + if (!p_GetVolumePathNameW || !p_GetVolumePathNameW((LPCWSTR)path.c_str(), volumeRootPath, MAX_PATH)) { + Dmsg1(50, "Failed GetVolumePathNameW path=%ls\n", path.c_str()); + return L""; + } + rootPath.assign(volumeRootPath); + Dmsg1(dbglvl_snap, "VolumeRootPath=%ls\n", volumeRootPath); + + // Get the volume name alias (might be different from the unique volume name in rare cases) + if (!p_GetVolumeNameForVolumeMountPointW || !p_GetVolumeNameForVolumeMountPointW(volumeRootPath, volumeName, MAX_PATH)) { + Dmsg1(50, "Failed GetVolumeNameForVolumeMountPointW path=%ls\n", volumeRootPath); + return L""; + } + Dmsg1(dbglvl_snap, "VolumeName=%ls\n", volumeName); + + // Get the unique volume name + if (!p_GetVolumeNameForVolumeMountPointW(volumeName, volumeUniqueName, MAX_PATH)) { + Dmsg1(50, "Failed GetVolumeNameForVolumeMountPointW path=%ls\n", volumeName); + return L""; + } + Dmsg1(dbglvl_snap, "VolumeUniqueName=%ls\n", volumeUniqueName); + return volumeUniqueName; + } + + int volume_search(void *i1, void *i2) + { + wstring *volname = (wstring *) i1; + MTabEntry *vol = (MTabEntry *) i2; + + return volname->compare(vol->volumeName); + } + + int volume_cmp(void *e1, void *e2) + { + MTabEntry *v1 = (MTabEntry *) e1; + MTabEntry *v2 = (MTabEntry *) e2; + return wcscmp(v1->volumeName, v2->volumeName); + } +} + +MTab::MTab() { + MTabEntry *elt = NULL; + lasterror = ERROR_SUCCESS; + lasterror_str = ""; + nb_in_SnapshotSet = 0; + entries = New(rblist(elt, &elt->link)); +} + +MTab::~MTab() { + if (entries) { + MTabEntry *elt; + foreach_rblist(elt, entries) { + elt->destroy(); + } + delete entries; + } +} + +UINT MTabEntry::getDriveType() +{ + WCHAR *root = first(); + + // Make sure to discard CD-ROM and network drives + if (!root) { + return 0; + } + + driveType = GetDriveTypeW(root); + return driveType; +} + +/* Return true if the current volume can be snapshoted (ie not CDROM or fat32) */ +bool MTabEntry::isSuitableForSnapshot() +{ + DWORD componentlength, fsflags; + WCHAR fstype[50]; + WCHAR *root = first(); + UINT oldmode; + BOOL result; + + // Make sure to discard CD-ROM and network drives + if (!root) { + Dmsg1(dbglvl_snap, "No mount point for %ls\n", volumeName); + goto bail_out; + } + + if (getDriveType() != DRIVE_FIXED) { + Dmsg2(dbglvl_snap, "Invalid disk type %d for %ls\n", driveType, root); + goto bail_out; + } + + /* From fstype.c, except that we have WCHAR instead of char */ + /* We don't want any popups if there isn't any media in the drive */ + oldmode = SetErrorMode(SEM_FAILCRITICALERRORS); + result = GetVolumeInformationW(root, NULL, 0, NULL, + &componentlength, &fsflags, fstype, ARRAYSIZE(fstype)); + SetErrorMode(oldmode); + + if (result) { + /* Windows returns NTFS, FAT, etc. Make it lowercase to be consistent with other OSes */ + Dmsg1(dbglvl_snap, "fstype=%ls\n", fstype); + if (!_wcsicmp(fstype, L"ntfs")) { + can_Snapshot = true; + } + if (!_wcsicmp(fstype, L"refs")) { + can_Snapshot = true; + } + if (!_wcsicmp(fstype, L"csvfs")) { + can_Snapshot = true; + } + } +bail_out: + Dmsg2(dbglvl_snap, "%ls is %s suitable for VSS snapshot\n", root, can_Snapshot?"":"not"); + return can_Snapshot; +} + +/* Find a volume for a specific path */ +MTabEntry *MTab::search(char *p) +{ + wstring volume; + wstring path; + wstring rootPath; + + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + wutf8_path_2_wchar(&pwszBuf, p); + path.assign((wchar_t *)pwszBuf); + volume = GetUniqueVolumeNameForPath(path, rootPath); + + MTabEntry *elt = (MTabEntry *)entries->search(&volume, volume_search); + free_pool_memory(pwszBuf); + + if (!elt) { + Dmsg1(000, "Unable to find %ls in volume list\n", volume.c_str()); + } + + return elt; +} + +bool MTab::addInSnapshotSet(char *p) +{ + MTabEntry *elt = search(p); + if (elt) { + if (!elt->in_SnapshotSet && elt->isSuitableForSnapshot()) { + nb_in_SnapshotSet++; + elt->setInSnapshotSet(); + } + } + return nb_in_SnapshotSet == entries->size(); +} + +/* Initialize the "entries" list will all existing volumes */ +bool MTab::get() +{ + DWORD count = 0; + WCHAR DeviceName[MAX_PATH] = L""; + HANDLE FindHandle = INVALID_HANDLE_VALUE; + size_t Index = 0; + bool Success = FALSE; + WCHAR VolumeName[MAX_PATH] = L""; + + Dmsg0(dbglvl_snap, "Filling MTAB\n"); + + + // Enumerate all volumes in the system. + FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName)); + + if (FindHandle == INVALID_HANDLE_VALUE) { + lasterror = GetLastError(); + return false; + } + + for (;;) { + // Skip the \\?\ prefix and remove the trailing backslash. + Index = wcslen(VolumeName) - 1; + + if (VolumeName[0] != L'\\' || + VolumeName[1] != L'\\' || + VolumeName[2] != L'?' || + VolumeName[3] != L'\\' || + VolumeName[Index] != L'\\') + { + lasterror = ERROR_BAD_PATHNAME; + lasterror_str = "FindFirstVolumeW/FindNextVolumeW returned a bad path"; + Dmsg1(000, "FindFirstVolumeW/FindNextVolumeW returned a bad path %ls\n", VolumeName); + break; + } + + // + // QueryDosDeviceW does not allow a trailing backslash, + // so temporarily remove it. + VolumeName[Index] = L'\0'; + + count = QueryDosDeviceW(&VolumeName[4], DeviceName, + ARRAYSIZE(DeviceName)); + + VolumeName[Index] = L'\\'; + + if (count == 0) { + lasterror = GetLastError(); + Dmsg1(000, "QueryDosDeviceW failed with error code %d\n", lasterror); + break; + } + + // VolumeName='\\\\?\\Volume{086fc09e-7781-4076-9b2e-a4ff5c6b52c7}\\' + // DeviceName='\\Device\\HarddiskVolume2' + MTabEntry *entry = New(MTabEntry(DeviceName, VolumeName)); + entries->insert(entry, volume_cmp); + + // + // Move on to the next volume. + Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName)); + + if (!Success) { + lasterror = GetLastError(); + if (lasterror != ERROR_NO_MORE_FILES) { + Dmsg1(000, "FindNextVolumeW failed with error code %d\n", lasterror); + break; + } + + // Finished iterating + // through all the volumes. + lasterror = ERROR_SUCCESS; + break; + } + } + + FindVolumeClose(FindHandle); + FindHandle = INVALID_HANDLE_VALUE; + + return true; +} diff --git a/bacula/src/win32/compat/mtab.h b/bacula/src/win32/compat/mtab.h new file mode 100644 index 000000000..8fe1dd8ae --- /dev/null +++ b/bacula/src/win32/compat/mtab.h @@ -0,0 +1,174 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2020 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ + +#ifndef BEE_MTAB_ENTRY_H +#define BEE_MTAB_ENTRY_H + +#include "winapi.h" + +#define bwcsdup(str) wcscpy((WCHAR *)bmalloc((wcslen(str)+1)*sizeof(WCHAR)),(str)) +#define safe_bfree(buf) if (buf) { bfree(buf); buf = NULL; } + +/* The MTabEntry class is representing a mounted volume, + * it associates a volume name with mount paths and a device name + */ +class MTabEntry: public SMARTALLOC { +public: + WCHAR *volumeName; // Name of the current volume + WCHAR *mountPaths; // List of mount paths + WCHAR *deviceName; + WCHAR *shadowCopyName; + bool in_SnapshotSet; + bool can_Snapshot; + UINT driveType; + rblink link; + + MTabEntry() { + mountPaths = NULL; + volumeName = NULL; + deviceName = NULL; + in_SnapshotSet = false; + shadowCopyName = NULL; + can_Snapshot = false; + driveType = 0; + }; + + MTabEntry(WCHAR *DeviceName, WCHAR *VolumeName) { + int last = wcslen(VolumeName); + if (VolumeName[last - 1] == L'\\') { + volumeName = bwcsdup(VolumeName); + } else { /* \\ + \0 */ + volumeName = (WCHAR *)bmalloc((last+1)*sizeof(WCHAR)); + wcscpy(volumeName, VolumeName); + volumeName[last] = L'\\'; + volumeName[last+1] = L'\0'; + } + mountPaths = NULL; + in_SnapshotSet = false; + deviceName = bwcsdup(DeviceName); + shadowCopyName = NULL; + driveType = 0; + can_Snapshot = false; + get_paths(); + }; + + ~MTabEntry() { + destroy(); + }; + + void destroy() { + safe_bfree(mountPaths); + safe_bfree(volumeName); + safe_bfree(deviceName); + safe_bfree(shadowCopyName); + }; + + /* Return the drive type (cdrom, fixed, network, ...) */ + UINT getDriveType(); + + /* Return true if the current volume can be snapshoted (ie not CDROM or fat32) */ + bool isSuitableForSnapshot(); + + void setInSnapshotSet() { + Dmsg1(050, "Marking %ls for the SnapshotSet\n", mountPaths); + in_SnapshotSet = true; + } + + void debug_paths() { + WCHAR *p; + /* Display the paths in the list. */ + if (mountPaths != NULL) { + Dmsg2(DT_VOLUME|10, "Device: [%ls], Volume: [%ls]\n", deviceName, volumeName); + for ( p = mountPaths; p[0] != L'\0'; p += wcslen(p) + 1) { + Dmsg1(DT_VOLUME|10, " %ls\n", p); + } + } + }; + + /* Compute the path list assiciated with the current volume */ + bool get_paths() { + DWORD count = MAX_PATH + 1; + bool ret = false; + + for (;;) { + // Allocate a buffer to hold the paths. + mountPaths = (WCHAR*) malloc(count * sizeof(WCHAR)); + + // Obtain all of the paths + // for this volume. + ret = GetVolumePathNamesForVolumeNameW(volumeName, mountPaths, + count, &count); + if (ret) { + break; + } + + if (GetLastError() != ERROR_MORE_DATA) { + break; + } + + // Try again with the + // new suggested size. + free(mountPaths); + mountPaths = NULL; + } + debug_paths(); + return ret; + }; + + /* Return the first mount point */ + WCHAR *first() { + return mountPaths; + }; + + /* Return the next mount point */ + WCHAR *next(WCHAR *prev) { + if (prev == NULL || prev[0] == L'\0') { + return NULL; + } + + prev += wcslen(prev) + 1; + + return (prev[0] == L'\0') ? NULL : prev; + }; +}; + +/* Class to handle all volumes of the system, it contains + * a list of all current volumes (MTabEntry) + */ +class MTab: public SMARTALLOC { +public: + DWORD lasterror; + const char *lasterror_str; + rblist *entries; /* MTabEntry */ + int nb_in_SnapshotSet; + + MTab(); + ~MTab(); + + /* Get a Volume by name */ + MTabEntry *search(char *file); + + /* Try to add a volume to the current snapshotset */ + bool addInSnapshotSet(char *file); + + /* Fill the "entries" list will all detected volumes of the system*/ + bool get(); +}; + +#endif /* BEE_MTAB_ENTRY_H */ diff --git a/bacula/src/win32/filed/Makefile b/bacula/src/win32/filed/Makefile index 8f6c2a0ca..20d02e1ab 100644 --- a/bacula/src/win32/filed/Makefile +++ b/bacula/src/win32/filed/Makefile @@ -58,6 +58,7 @@ FILED_OBJS = \ $(OBJDIR)/heartbeat.o \ $(OBJDIR)/hello.o \ $(OBJDIR)/job.o \ + $(OBJDIR)/mtab.o \ $(OBJDIR)/restore.o \ $(OBJDIR)/status.o \ $(OBJDIR)/verify.o \ diff --git a/bacula/src/win32/filed/vss.cpp b/bacula/src/win32/filed/vss.cpp index 155f069e1..0d7f17913 100644 --- a/bacula/src/win32/filed/vss.cpp +++ b/bacula/src/win32/filed/vss.cpp @@ -48,184 +48,6 @@ static int volume_search(void *i1, void *i2) return volname->compare(vol->volumeName); } -static int volume_cmp(void *e1, void *e2) -{ - MTabEntry *v1 = (MTabEntry *) e1; - MTabEntry *v2 = (MTabEntry *) e2; - return wcscmp(v1->volumeName, v2->volumeName); -} - -UINT MTabEntry::getDriveType() -{ - WCHAR *root = first(); - - // Make sure to discard CD-ROM and network drives - if (!root) { - return 0; - } - - driveType = GetDriveTypeW(root); - return driveType; -} - -/* Return true if the current volume can be snapshoted (ie not CDROM or fat32) */ -bool MTabEntry::isSuitableForSnapshot() -{ - DWORD componentlength, fsflags; - WCHAR fstype[50]; - WCHAR *root = first(); - UINT oldmode; - BOOL result; - - // Make sure to discard CD-ROM and network drives - if (!root) { - Dmsg1(dbglvl_snap, "No mount point for %ls\n", volumeName); - goto bail_out; - } - - if (getDriveType() != DRIVE_FIXED) { - Dmsg2(dbglvl_snap, "Invalid disk type %d for %ls\n", driveType, root); - goto bail_out; - } - - /* From fstype.c, except that we have WCHAR instead of char */ - /* We don't want any popups if there isn't any media in the drive */ - oldmode = SetErrorMode(SEM_FAILCRITICALERRORS); - result = GetVolumeInformationW(root, NULL, 0, NULL, - &componentlength, &fsflags, fstype, ARRAYSIZE(fstype)); - SetErrorMode(oldmode); - - if (result) { - /* Windows returns NTFS, FAT, etc. Make it lowercase to be consistent with other OSes */ - Dmsg1(dbglvl_snap, "fstype=%ls\n", fstype); - if (!_wcsicmp(fstype, L"ntfs")) { - can_Snapshot = true; - } - if (!_wcsicmp(fstype, L"refs")) { - can_Snapshot = true; - } - } -bail_out: - Dmsg2(dbglvl_snap, "%ls is %s suitable for VSS snapshot\n", root, can_Snapshot?"":"not"); - return can_Snapshot; -} - -/* Find a volume for a specific path */ -MTabEntry *MTab::search(char *p) -{ - wstring volume; - wstring path; - wstring rootPath; - - POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); - wutf8_path_2_wchar(&pwszBuf, p); - path.assign((wchar_t *)pwszBuf); - volume = GetUniqueVolumeNameForPath(path, rootPath); - - MTabEntry *elt = (MTabEntry *)entries->search(&volume, volume_search); - free_pool_memory(pwszBuf); - - if (!elt) { - Dmsg1(000, "Unable to find %ls in volume list\n", volume.c_str()); - } - - return elt; -} - -bool MTab::addInSnapshotSet(char *p) -{ - MTabEntry *elt = search(p); - if (elt) { - if (!elt->in_SnapshotSet && elt->isSuitableForSnapshot()) { - nb_in_SnapshotSet++; - elt->setInSnapshotSet(); - } - } - return nb_in_SnapshotSet == entries->size(); -} - -/* Initialize the "entries" list will all existing volumes */ -bool MTab::get() -{ - DWORD count = 0; - WCHAR DeviceName[MAX_PATH] = L""; - HANDLE FindHandle = INVALID_HANDLE_VALUE; - size_t Index = 0; - bool Success = FALSE; - WCHAR VolumeName[MAX_PATH] = L""; - - Dmsg0(dbglvl_snap, "Filling MTAB\n"); - - - // Enumerate all volumes in the system. - FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName)); - - if (FindHandle == INVALID_HANDLE_VALUE) { - lasterror = GetLastError(); - return false; - } - - for (;;) { - // Skip the \\?\ prefix and remove the trailing backslash. - Index = wcslen(VolumeName) - 1; - - if (VolumeName[0] != L'\\' || - VolumeName[1] != L'\\' || - VolumeName[2] != L'?' || - VolumeName[3] != L'\\' || - VolumeName[Index] != L'\\') - { - lasterror = ERROR_BAD_PATHNAME; - lasterror_str = "FindFirstVolumeW/FindNextVolumeW returned a bad path"; - Dmsg1(000, "FindFirstVolumeW/FindNextVolumeW returned a bad path %ls\n", VolumeName); - break; - } - - // - // QueryDosDeviceW does not allow a trailing backslash, - // so temporarily remove it. - VolumeName[Index] = L'\0'; - - count = QueryDosDeviceW(&VolumeName[4], DeviceName, - ARRAYSIZE(DeviceName)); - - VolumeName[Index] = L'\\'; - - if (count == 0) { - lasterror = GetLastError(); - Dmsg1(000, "QueryDosDeviceW failed with error code %d\n", lasterror); - break; - } - - // VolumeName='\\\\?\\Volume{086fc09e-7781-4076-9b2e-a4ff5c6b52c7}\\' - // DeviceName='\\Device\\HarddiskVolume2' - MTabEntry *entry = New(MTabEntry(DeviceName, VolumeName)); - entries->insert(entry, volume_cmp); - - // - // Move on to the next volume. - Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName)); - - if (!Success) { - lasterror = GetLastError(); - if (lasterror != ERROR_NO_MORE_FILES) { - Dmsg1(000, "FindNextVolumeW failed with error code %d\n", lasterror); - break; - } - - // Finished iterating - // through all the volumes. - lasterror = ERROR_SUCCESS; - break; - } - } - - FindVolumeClose(FindHandle); - FindHandle = INVALID_HANDLE_VALUE; - - return true; -} - BOOL VSSPathConverter(); BOOL VSSPathConvert(const char *szFilePath, char *szShadowPath, int nBuflen); BOOL VSSPathConvertW(const wchar_t *szFilePath, wchar_t *szShadowPath, int nBuflen); diff --git a/bacula/src/win32/filed/vss.h b/bacula/src/win32/filed/vss.h index 90be31850..4ec40e8fe 100644 --- a/bacula/src/win32/filed/vss.h +++ b/bacula/src/win32/filed/vss.h @@ -37,175 +37,11 @@ #ifdef WIN32_VSS -#define VSS_INIT_RESTORE_AFTER_INIT 1 -#define VSS_INIT_RESTORE_AFTER_GATHER 2 +#include "mtab.h" // some forward declarations struct IVssAsync; -#define bwcsdup(str) wcscpy((WCHAR *)bmalloc((wcslen(str)+1)*sizeof(WCHAR)),(str)) -#define safe_bfree(buf) if (buf) { bfree(buf); buf = NULL; } - -/* The MTabEntry class is representing a mounted volume, - * it associates a volume name with mount paths and a device name - */ -class MTabEntry: public SMARTALLOC { -public: - WCHAR *volumeName; // Name of the current volume - WCHAR *mountPaths; // List of mount paths - WCHAR *deviceName; - WCHAR *shadowCopyName; - bool in_SnapshotSet; - bool can_Snapshot; - UINT driveType; - rblink link; - - MTabEntry() { - mountPaths = NULL; - volumeName = NULL; - deviceName = NULL; - in_SnapshotSet = false; - shadowCopyName = NULL; - can_Snapshot = false; - driveType = 0; - }; - - MTabEntry(WCHAR *DeviceName, WCHAR *VolumeName) { - int last = wcslen(VolumeName); - if (VolumeName[last - 1] == L'\\') { - volumeName = bwcsdup(VolumeName); - } else { /* \\ + \0 */ - volumeName = (WCHAR *)bmalloc((last+1)*sizeof(WCHAR)); - wcscpy(volumeName, VolumeName); - volumeName[last] = L'\\'; - volumeName[last+1] = L'\0'; - } - mountPaths = NULL; - in_SnapshotSet = false; - deviceName = bwcsdup(DeviceName); - shadowCopyName = NULL; - driveType = 0; - can_Snapshot = false; - get_paths(); - }; - - ~MTabEntry() { - destroy(); - }; - - void destroy() { - safe_bfree(mountPaths); - safe_bfree(volumeName); - safe_bfree(deviceName); - safe_bfree(shadowCopyName); - }; - - /* Return the drive type (cdrom, fixed, network, ...) */ - UINT getDriveType(); - - /* Return true if the current volume can be snapshoted (ie not CDROM or fat32) */ - bool isSuitableForSnapshot(); - - void setInSnapshotSet() { - Dmsg1(050, "Marking %ls for the SnapshotSet\n", mountPaths); - in_SnapshotSet = true; - } - - void debug_paths() { - WCHAR *p; - /* Display the paths in the list. */ - if (mountPaths != NULL) { - Dmsg2(DT_VOLUME|10, "Device: [%ls], Volume: [%ls]\n", deviceName, volumeName); - for ( p = mountPaths; p[0] != L'\0'; p += wcslen(p) + 1) { - Dmsg1(DT_VOLUME|10, " %ls\n", p); - } - } - }; - - /* Compute the path list assiciated with the current volume */ - bool get_paths() { - DWORD count = MAX_PATH + 1; - bool ret = false; - - for (;;) { - // Allocate a buffer to hold the paths. - mountPaths = (WCHAR*) malloc(count * sizeof(WCHAR)); - - // Obtain all of the paths - // for this volume. - ret = GetVolumePathNamesForVolumeNameW(volumeName, mountPaths, - count, &count); - if (ret) { - break; - } - - if (GetLastError() != ERROR_MORE_DATA) { - break; - } - - // Try again with the - // new suggested size. - free(mountPaths); - mountPaths = NULL; - } - debug_paths(); - return ret; - }; - - /* Return the first mount point */ - WCHAR *first() { - return mountPaths; - }; - - /* Return the next mount point */ - WCHAR *next(WCHAR *prev) { - if (prev == NULL || prev[0] == L'\0') { - return NULL; - } - - prev += wcslen(prev) + 1; - - return (prev[0] == L'\0') ? NULL : prev; - }; -}; - -/* Class to handle all volumes of the system, it contains - * a list of all current volumes (MTabEntry) - */ -class MTab: public SMARTALLOC { -public: - DWORD lasterror; - const char *lasterror_str; - rblist *entries; /* MTabEntry */ - int nb_in_SnapshotSet; - - MTab() { - MTabEntry *elt = NULL; - lasterror = ERROR_SUCCESS; - lasterror_str = ""; - nb_in_SnapshotSet = 0; - entries = New(rblist(elt, &elt->link)); - }; - - ~MTab() { - if (entries) { - MTabEntry *elt; - foreach_rblist(elt, entries) { - elt->destroy(); - } - delete entries; - } - }; - /* Get a Volume by name */ - MTabEntry *search(char *file); - - /* Try to add a volume to the current snapshotset */ - bool addInSnapshotSet(char *file); - - /* Fill the "entries" list will all detected volumes of the system*/ - bool get(); -}; - class VSSClient { public: @@ -230,6 +66,7 @@ public: void AppendWriterInfo(int nState, const char* pszInfo); const bool IsInitialized() { return m_bBackupIsInitialized; }; IUnknown *GetVssObject() { return m_pVssObject; }; + MTab* GetVolumeList() { return m_VolumeList; }; private: virtual bool Initialize(DWORD dwContext, bool bDuringRestore = FALSE) = 0; diff --git a/bacula/src/win32/lib/Makefile b/bacula/src/win32/lib/Makefile index a1e6295dd..283a257ab 100644 --- a/bacula/src/win32/lib/Makefile +++ b/bacula/src/win32/lib/Makefile @@ -33,9 +33,8 @@ EXTRA_LIB_OBJS = $(addprefix $(OBJDIR)/, $(EXTRA_LIB_OBJS_ORG)) COMPAT_OBJS = \ $(OBJDIR)/compat.o \ $(OBJDIR)/print.o \ - $(OBJDIR)/winapi.o - -# $(OBJDIR)/getopt.o \ + $(OBJDIR)/winapi.o \ + $(OBJDIR)/mtab.o FIND_OBJS = \ $(OBJDIR)/attribs.o \