From a78ef121cc5267bd1bed8b3c1e487d164a4f8160 Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Fri, 5 Oct 2007 16:56:55 +0000 Subject: [PATCH] kes Fix listing performance problems in bat. Pointed out by Chris Howells. kes Remove old debug code. kes Fix bat code that tests for Win32. This should fix bug #968 kes Query 5 list wrong Vol after migration. This fixes bug #960 git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@5727 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/LICENSE | 16 ++++----- bacula/projects | 2 +- bacula/src/dird/query.sql | 4 +-- bacula/src/filed/job.c | 6 ---- bacula/src/qt-console/bat.h | 1 + bacula/src/qt-console/pages.cpp | 9 +++++ bacula/src/qt-console/restore/restore.cpp | 10 ++---- bacula/src/qt-console/restore/restoretree.cpp | 33 +++++++++---------- bacula/src/qt-console/restore/restoretree.h | 1 - bacula/src/stored/askdir.c | 26 +++++++-------- bacula/src/stored/dev.h | 5 +-- bacula/src/stored/reserve.c | 13 +++++--- bacula/src/tools/bsmtp.c | 2 +- bacula/src/version.h | 4 +-- bacula/technotes-2.3 | 13 +++++--- 15 files changed, 75 insertions(+), 70 deletions(-) diff --git a/bacula/LICENSE b/bacula/LICENSE index fea736568e..83f25f9a71 100644 --- a/bacula/LICENSE +++ b/bacula/LICENSE @@ -10,21 +10,19 @@ The name Bacula is a registered trademark. =================================== License: -For the most part, Bacula is licensed under the GPL version 2 -this code is listed under Copyright Free Software Foundation -Europe e.V. What follows are the addition(s) to the GPL version -2 license, for code that is copyrighted as noted above. +For the most part, Bacula is licensed under the GPL version 2 this +code is listed under Copyright Free Software Foundation Europe e.V. +What follows is the addition(s) to the GPL version 2 license, that +applys to code that is copyrighted by the Free Software Foundation +Europe e.V. Linking: As a special exception to the GPLv2, the Bacula Project gives permission to link the code of its release of Bacula with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked -executables. You must obey the GNU General Public License in all -respects for all of the code used other than "OpenSSL". If you modify -a particular file, you may extend this exception to your version of the file, -but you are not obligated to do so. If you do not wish this exception -to apply to your code, clearly indicate so in the file. +executables. You must obey the GNU General Public License in all +respects for all of the code used other than "OpenSSL". =================================== diff --git a/bacula/projects b/bacula/projects index 6b99f5fc6a..55820d6946 100644 --- a/bacula/projects +++ b/bacula/projects @@ -38,7 +38,7 @@ Item 25: Archival (removal) of User Files to Tape Item 1: Accurate restoration of renamed/deleted files Date: 28 November 2005 Origin: Martin Simmons (martin at lispworks dot com) - Status: Robert Nelson will implement this + Status: What: When restoring a fileset for a specified date (including "most recent"), Bacula should give you exactly the files and directories diff --git a/bacula/src/dird/query.sql b/bacula/src/dird/query.sql index c9f27f9893..b18f10fd65 100644 --- a/bacula/src/dird/query.sql +++ b/bacula/src/dird/query.sql @@ -56,8 +56,8 @@ SELECT DISTINCT Job.JobId as JobId,Client.Name as Client, JobFiles,JobBytes,VolumeName FROM Client,Job,JobMedia,Media,FileSet WHERE Client.Name='%1' - AND Client.ClientId=Job.ClientId - AND JobStatus='T' AND Job.FileSetId=FileSet.FileSetId + AND Client.ClientId=Job.ClientId AND Job.Type='B' + AND Job.JobStatus='T' AND Job.FileSetId=FileSet.FileSetId AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId ORDER BY Job.StartTime; # 6 diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index d2f5097102..03c971d8e8 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -437,9 +437,6 @@ static int job_cmd(JCR *jcr) POOLMEM *sd_auth_key; sd_auth_key = get_memory(dir->msglen); - if (debug_level == 3) { - Dmsg1(000, "msg); - } if (sscanf(dir->msg, jobcmd, &jcr->JobId, jcr->Job, &jcr->VolSessionId, &jcr->VolSessionTime, sd_auth_key) != 5) { @@ -450,9 +447,6 @@ static int job_cmd(JCR *jcr) return 0; } jcr->sd_auth_key = bstrdup(sd_auth_key); - if (debug_level == 3) { - Dmsg1(000, "sd_auth_key=%s\n", jcr->sd_auth_key); - } free_pool_memory(sd_auth_key); Dmsg2(120, "JobId=%d Auth=%s\n", jcr->JobId, jcr->sd_auth_key); return dir->fsend(OKjob, VERSION, LSMDATE, HOST_OS, DISTNAME, DISTVER); diff --git a/bacula/src/qt-console/bat.h b/bacula/src/qt-console/bat.h index 608b15cc60..650f6fec19 100644 --- a/bacula/src/qt-console/bat.h +++ b/bacula/src/qt-console/bat.h @@ -48,5 +48,6 @@ extern MainWin *mainWin; extern QApplication *app; int bvsnprintf(char *str, int32_t size, const char *format, va_list ap); +bool isWin32Path(QString &fullPath); #endif /* _BAT_H_ */ diff --git a/bacula/src/qt-console/pages.cpp b/bacula/src/qt-console/pages.cpp index 157ea5b253..59c5143e59 100644 --- a/bacula/src/qt-console/pages.cpp +++ b/bacula/src/qt-console/pages.cpp @@ -34,6 +34,15 @@ #include "pages.h" #include "bat.h" +/* A global function */ +bool isWin32Path(QString &fullPath) +{ + char *buf = fullPath.left(2).toUtf8().data(); + + return buf[1] == ':' && B_ISALPHA(buf[0]); +} + + /* * dockPage * This function is intended to be called from within the Pages class to pull diff --git a/bacula/src/qt-console/restore/restore.cpp b/bacula/src/qt-console/restore/restore.cpp index 26114225db..73661b4e06 100644 --- a/bacula/src/qt-console/restore/restore.cpp +++ b/bacula/src/qt-console/restore/restore.cpp @@ -181,7 +181,6 @@ void restorePage::addDirectory(QString &newdirr) { QString newdir = newdirr; QString fullpath = m_cwd + newdirr; - QRegExp regex("^/[a-z]:/$"); bool ok = true; bool windrive = false; @@ -193,7 +192,7 @@ void restorePage::addDirectory(QString &newdirr) } /* add unix '/' directory first */ - if (m_dirPaths.empty() && (regex.indexIn(fullpath,0) == -1)) { + if (m_dirPaths.empty() && !isWin32Path(fullpath)) { QTreeWidgetItem *item = new QTreeWidgetItem(directoryWidget); item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png"))); @@ -206,12 +205,11 @@ void restorePage::addDirectory(QString &newdirr) m_dirTreeItems.insert(item, text); } - if (regex.indexIn(fullpath,0) == 0) { + if (isWin32Path(fullpath)) { /* this is a windows drive */ if (mainWin->m_miscDebug) { Pmsg0(000, "Need to do windows \"letter\":/\n"); } - fullpath.replace(0,1,""); windrive = true; } @@ -316,10 +314,6 @@ void restorePage::fileDoubleClicked(QTreeWidgetItem *item, int column) */ if (item->text(1).endsWith("/")) { QString fullpath = m_cwd + item->text(1); - /* check for fullpath = "/c:/" */ - QRegExp regex("^/[a-z]:/"); - if (regex.indexIn(fullpath,0) == 0) /* remove leading '/' */ - fullpath.replace(0,1,""); QTreeWidgetItem *item = m_dirPaths.value(fullpath); if (item) { directoryWidget->setCurrentItem(item); diff --git a/bacula/src/qt-console/restore/restoretree.cpp b/bacula/src/qt-console/restore/restoretree.cpp index e1594a39b9..0bec98106e 100644 --- a/bacula/src/qt-console/restore/restoretree.cpp +++ b/bacula/src/qt-console/restore/restoretree.cpp @@ -51,9 +51,6 @@ restoreTree::restoreTree() m_populated = false; dockPage(); - m_winRegExpDrive.setPattern("^[a-zA-Z]:/$"); - m_winRegExpPath.setPattern("^[a-zA-Z]:/"); - m_slashregex.setPattern("/"); m_debugCnt = 0; m_debugTrap = true; @@ -259,8 +256,10 @@ void restoreTree::populateDirectoryTree() Pmsg1(000, "Done with query %i results\n", results.count()); QStringList fieldlist; foreach(QString resultline, results) { - m_debugCnt += 1; - prBar2->setValue(m_debugCnt); + /* Update progress bar periodically */ + if ((++m_debugCnt && 0x3FF) == 0) { + prBar2->setValue(m_debugCnt); + } fieldlist = resultline.split("\t"); int fieldcnt = 0; QString field; @@ -324,15 +323,14 @@ void restoreTree::setJobsCheckedList() */ void restoreTree::parseDirectory(QString &dir_in) { - /* m_debugTrap is to only print debugs for a few occurances of calling parseDirectory + /* m_debugTrap is to only print debugs for a few occurennces of calling parseDirectory * instead of printing out what could potentially a whole bunch */ if (m_debugCnt > 2) m_debugTrap = false; - /* Clean up the directory string remove some funny char after last '/' */ - QRegExp rgx("[^/]$"); - int lastslash = rgx.indexIn(dir_in); - if (lastslash != -1) - dir_in.replace(lastslash, dir_in.length()-lastslash, ""); + /* Truncate everything after the last / */ + if (dir_in.right(1) != "/") { + dir_in.truncate(dir_in.lastIndexOf("/") + 1); + } if ((mainWin->m_miscDebug) && (m_debugTrap)) Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data()); @@ -344,7 +342,7 @@ void restoreTree::parseDirectory(QString &dir_in) /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/ * if not added into tree, then try /etc/ and somedir/ if not added, then try * / and etc/ . That should succeed, then add the ones that failed in reverse */ - while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) { + while (((index = dir_in.lastIndexOf("/", -2)) != -1) && (!done)) { direct = path = dir_in; path.replace(index+1, dir_in.length()-index-1,""); direct.replace(0, index+1, ""); @@ -373,6 +371,7 @@ void restoreTree::parseDirectory(QString &dir_in) } } + /* * Function called from fill directory when a directory is found to see if this * directory exists in the directory pane and then add it to the directory pane @@ -392,7 +391,7 @@ bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr) if (!m_slashTrap) { /* add unix '/' directory first */ - if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullPath, 0) == -1)) { + if (m_dirPaths.empty() && isWin32Path(fullPath)) { m_slashTrap = true; QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree); QString text("/"); @@ -406,7 +405,7 @@ bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr) m_dirPaths.insert(text, item); } /* no need to check for windows drive if unix */ - if (m_winRegExpDrive.indexIn(m_cwd, 0) == 0) { + if (isWin32Path(m_cwd)) { if (!m_dirPaths.contains(m_cwd)) { /* this is a windows drive add the base widget */ QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree); @@ -1280,7 +1279,7 @@ void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in bool done = false; QString fullPath = fullPath_in; QString direct, path; - while (((index = m_slashregex.lastIndexIn(fullPath, -2)) != -1) && (!done)) { + while (((index = fullPath.lastIndexOf("/", -2)) != -1) && (!done)) { direct = path = fullPath; path.replace(index+1, fullPath.length()-index-1, ""); direct.replace(0, index+1, ""); @@ -1730,7 +1729,7 @@ int restoreTree::mostRecentVersionfromFullPath(QString &fullPath) { int qversion = 0; QString directory, fileName; - int index = m_slashregex.lastIndexIn(fullPath, -2); + int index = fullPath.lastIndexOf("/", -2); if (index != -1) { directory = fileName = fullPath; directory.replace(index+1, fullPath.length()-index-1, ""); @@ -1783,7 +1782,7 @@ int restoreTree::queryFileIndex(QString &fullPath, int jobId) { int qfileIndex = 0; QString directory, fileName; - int index = m_slashregex.lastIndexIn(fullPath, -2); + int index = fullPath.lastIndexOf("/", -2); if (index != -1) { directory = fileName = fullPath; directory.replace(index+1, fullPath.length()-index-1, ""); diff --git a/bacula/src/qt-console/restore/restoretree.h b/bacula/src/qt-console/restore/restoretree.h index be99c492e2..1ad1f176ea 100644 --- a/bacula/src/qt-console/restore/restoretree.h +++ b/bacula/src/qt-console/restore/restoretree.h @@ -102,7 +102,6 @@ private: bool m_dropdownChanged; QRegExp m_winRegExpDrive; QRegExp m_winRegExpPath; - QRegExp m_slashregex; bool m_slashTrap; QHash m_dirPaths; QString m_checkedJobs, m_prevJobCombo, m_prevClientCombo, m_prevFileSetCombo; diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index 7f965e805f..1141df69c4 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -179,7 +179,7 @@ static bool do_get_volume_info(DCR *dcr) return false; } memset(&vol, 0, sizeof(vol)); - Dmsg2(100, "JobId, dir->msg); + Dmsg1(100, "msg); n = sscanf(dir->msg, OK_media, vol.VolCatName, &vol.VolCatJobs, &vol.VolCatFiles, &vol.VolCatBlocks, &vol.VolCatBytes, @@ -191,8 +191,8 @@ static bool do_get_volume_info(DCR *dcr) &vol.EndFile, &vol.EndBlock, &vol.VolCatParts, &vol.LabelType, &vol.VolMediaId); if (n != 22) { - Dmsg4(100, "Bad response from Dir jid=%u fields=%d, len=%d: %s", - (uint32_t)jcr->JobId, n, dir->msglen, dir->msg); + Dmsg3(100, "Bad response from Dir fields=%d, len=%d: %s", + n, dir->msglen, dir->msg); Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg); return false; } @@ -201,8 +201,8 @@ static bool do_get_volume_info(DCR *dcr) bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName)); dcr->VolCatInfo = vol; /* structure assignment */ - Dmsg3(100, "do_reqest_vol_info return true jid=%u slot=%d Volume=%s\n", - (uint32_t)jcr->JobId, vol.Slot, vol.VolCatName); + Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n", + vol.Slot, vol.VolCatName); return true; } @@ -227,7 +227,7 @@ bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) bash_spaces(dcr->VolCatInfo.VolCatName); dir->fsend(Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName, writing==GET_VOL_INFO_FOR_WRITE?1:0); - Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); + Dmsg1(100, ">dird %s", dir->msg); unbash_spaces(dcr->VolCatInfo.VolCatName); bool ok = do_get_volume_info(dcr); V(vol_info_mutex); @@ -271,14 +271,14 @@ bool dir_find_next_appendable_volume(DCR *dcr) dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type); unbash_spaces(dcr->media_type); unbash_spaces(dcr->pool_name); - Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); + Dmsg1(100, ">dird %s", dir->msg); bool ok = do_get_volume_info(dcr); if (ok) { if (!is_volume_in_use(dcr)) { found = true; break; } else { - Dmsg2(100, "jid=%u Volume %s is in use.\n", (uint32_t)jcr->JobId, dcr->VolumeName); + Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName); dcr->volume_in_use = true; continue; } @@ -358,7 +358,7 @@ bool dir_update_volume_info(DCR *dcr, bool label) edit_int64(vol->VolWriteTime, ed4), edit_uint64(vol->VolFirstWritten, ed5), vol->VolCatParts); - Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); + Dmsg1(100, ">dird %s", dir->msg); /* Do not lock device here because it may be locked from label */ if (!do_get_volume_info(dcr)) { @@ -367,7 +367,7 @@ bool dir_update_volume_info(DCR *dcr, bool label) vol->VolCatName, jcr->errmsg); goto bail_out; } - Dmsg2(420, "get_volume_info() jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); + Dmsg1(420, "get_volume_info() %s", dir->msg); /* Update dev Volume info in case something changed (e.g. expired) */ dev->VolCatInfo = dcr->VolCatInfo; ok = true; @@ -402,14 +402,14 @@ bool dir_create_jobmedia_record(DCR *dcr) dcr->StartBlock, dcr->EndBlock, dcr->Copy, dcr->Stripe, edit_uint64(dcr->VolMediaId, ed1)); - Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); + Dmsg1(100, ">dird %s", dir->msg); if (bnet_recv(dir) <= 0) { Dmsg0(190, "create_jobmedia error bnet_recv\n"); Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"), dir->bstrerror()); return false; } - Dmsg2(100, "JobId, dir->msg); + Dmsg1(100, "msg); if (strcmp(dir->msg, OK_create) != 0) { Dmsg1(130, "Bad response from Dir: %s\n", dir->msg); Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg); @@ -444,7 +444,7 @@ bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) ser_uint32(rec->data_len); ser_bytes(rec->data, rec->data_len); dir->msglen = ser_length(dir->msg); - Dmsg2(1800, ">dird jid=%u: %s\n", (uint32_t)jcr->JobId, dir->msg); /* Attributes */ + Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */ return dir->send(); } diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index f51f3ce160..45cd39f6ac 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -473,8 +473,9 @@ public: class VOLRES { public: dlink link; - char *vol_name; - DEVICE *dev; + char *vol_name; /* Volume name */ + DEVICE *dev; /* Pointer to device to which we are attached */ + bool released; /* set when the Volume can be released */ }; diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index 70083683bc..42b3434180 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -210,11 +210,11 @@ void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) if (dev) { len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name()); sendit(msg.c_str(), len, arg); - len = Mmsg(msg, " Reader=%d writers=%d reserved=%d\n", dev->can_read()?1:0, - dev->num_writers, dev->reserved_device); + len = Mmsg(msg, " Reader=%d writers=%d reserved=%d released=%d\n", + dev->can_read()?1:0, dev->num_writers, dev->reserved_device, vol->released); sendit(msg.c_str(), len, arg); } else { - len = Mmsg(msg, "%s no dev\n", vol->vol_name); + len = Mmsg(msg, "%s no device. released=%d\n", vol->vol_name, vol->released); sendit(msg.c_str(), len, arg); } } @@ -377,12 +377,16 @@ VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) Dmsg3(dbglvl, "Volume busy could not swap vol=%s from dev=%s to %s\n", VolumeName, vol->dev->print_name(), dev->print_name()); vol = NULL; /* device busy */ + goto get_out; } } } dev->vol = vol; get_out: + if (vol) { + vol->released = false; + } debug_list_volumes("end new volume"); unlock_volumes(); return vol; @@ -461,6 +465,7 @@ bool volume_unused(DCR *dcr) * explicitly read in this drive. This allows the SD to remember * where the tapes are or last were. */ + dev->vol->released = true; if (dev->is_tape() || dev->is_autochanger()) { return true; } else { @@ -512,7 +517,7 @@ void free_volume_list() if (vol->dev) { Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name()); } else { - Dmsg2(dbglvl, "free vol_list Volume=%s dev=%p\n", vol->vol_name, vol->dev); + Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name); } free(vol->vol_name); vol->vol_name = NULL; diff --git a/bacula/src/tools/bsmtp.c b/bacula/src/tools/bsmtp.c index 83b0e552dc..c1a3e22110 100644 --- a/bacula/src/tools/bsmtp.c +++ b/bacula/src/tools/bsmtp.c @@ -107,7 +107,7 @@ static char *cleanup_addr(char *addr, char *buf, int buf_len) { char *p, *q; - if ((p = strchr(from_addr, '<')) == NULL) { + if ((p = strchr(addr, '<')) == NULL) { snprintf(buf, buf_len, "<%s>", addr); } else { /* Copy */ diff --git a/bacula/src/version.h b/bacula/src/version.h index f1e59b8b66..502a193089 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -4,8 +4,8 @@ #undef VERSION #define VERSION "2.3.6" -#define BDATE "30 September 2007" -#define LSMDATE "30Sep07" +#define BDATE "05 October 2007" +#define LSMDATE "05Oct07" #define PROG_COPYRIGHT "Copyright (C) %d-2007 Free Software Foundation Europe e.V.\n" #define BYEAR "2007" /* year for copyright messages in progs */ diff --git a/bacula/technotes-2.3 b/bacula/technotes-2.3 index af38df92f7..d8a943c0e2 100644 --- a/bacula/technotes-2.3 +++ b/bacula/technotes-2.3 @@ -1,10 +1,15 @@ Technical notes on version 2.3 General: +05Oct07 +kes Fix listing performance problems in bat. Pointed out by + Chris Howells. +kes Remove old debug code. +kes Fix bat code that tests for Win32. This should fix bug #968 +kes Query 5 list wrong Vol after migration. This fixes bug #960 04Oct07 -ebl Fix #969 where user can't change Replace option in - restore menu. -30Sep07 +ebl Fix #969 where user can't change Replace option in restore menu. +5ASep07 kes Save jcr in thread specific data (tsd) for each thread. kes Make Dmsg() print JobId as -%u. kes Make Jmsg, Emsg, and others automatically pickup the jobid @@ -57,7 +62,7 @@ kes If Dir gets an error during inserting attributes, cancel SD. This reduces unnecessary error messages. 24Sep07 kes Correct search boolean for getting Volume info -ebl Cleanup batch code. Probably fixes bug #965. +ebl Cleanup batch insert code. Probably fixes bug #965. kes Back out one small change to the reservation system (reserving a volume). kes Rework how a Volume is mounted. It is now much more intelligent and will always attempt to use any mounted volume if possible and reduces -- 2.39.5