]> git.sur5r.net Git - bacula/bacula/commitdiff
Fix seg fault in Dir during estimate command with no level value
authorKern Sibbald <kern@sibbald.com>
Wed, 13 Aug 2008 14:36:31 +0000 (14:36 +0000)
committerKern Sibbald <kern@sibbald.com>
Wed, 13 Aug 2008 14:36:31 +0000 (14:36 +0000)
     given. This fixes bug #1140.

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@7473 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/patches/2.4.2-bat.patch [new file with mode: 0644]
bacula/patches/2.4.2-estimate-cmd.patch [new file with mode: 0644]
bacula/projects
bacula/src/dird/ua_cmds.c
bacula/src/version.h
bacula/technotes-2.5

diff --git a/bacula/patches/2.4.2-bat.patch b/bacula/patches/2.4.2-bat.patch
new file mode 100644 (file)
index 0000000..05ff853
--- /dev/null
@@ -0,0 +1,416 @@
+
+ This patch should clean up two problems with bat:
+   1. Eliminate print error output concerning job plotting
+   2. Eliminate the error messages where bat complains about being busy.
+ It also backports some new trunk code.
+ Apply it to Bacula version 2.4.2 with:
+
+  cd <bacula-source>
+  patch -p0 <2.4.2-bat.patch
+  ./configure <your-options>
+  make
+  ...
+  make install
+
+  
+Index: src/qt-console/mainwin.h
+===================================================================
+--- src/qt-console/mainwin.h   (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/mainwin.h   (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -107,9 +107,7 @@
+    void runButtonClicked();
+    void estimateButtonClicked();
+    void browseButtonClicked();
+-#ifdef HAVE_QWT
+    void jobPlotButtonClicked();
+-#endif
+    void restoreButtonClicked();
+    void undockWindowButton();
+    void treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *);
+Index: src/qt-console/fileset/fileset.h
+===================================================================
+--- src/qt-console/fileset/fileset.h   (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/fileset/fileset.h   (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -62,6 +62,7 @@
+    void createContextMenu();
+    QString m_currentlyselected;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkcurwidget;
+ };
+Index: src/qt-console/fileset/fileset.cpp
+===================================================================
+--- src/qt-console/fileset/fileset.cpp (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/fileset/fileset.cpp (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -50,6 +50,7 @@
+    /* mp_treeWidget, FileSet Tree Tree Widget inherited from ui_fileset.h */
+    m_populated = false;
++   m_populating = false;
+    m_checkcurwidget = true;
+    m_closeable = false;
+    readSettings();
+@@ -70,6 +71,10 @@
+  */
+ void FileSet::populateTree()
+ {
++   if (m_populating)
++      return;
++   m_populating = true;
++
+    QTreeWidgetItem *filesetItem, *topItem;
+    if (!m_console->preventInUseConnect())
+@@ -102,7 +107,7 @@
+            " FROM FileSet"
+            " WHERE ";
+       query += " FileSet='" + filesetName + "'";
+-      query += " ORDER BY FileSet";
++      query += " ORDER BY CreateTime DESC LIMIT 1";
+       QStringList results;
+       if (mainWin->m_sqlDebug) {
+@@ -135,7 +140,7 @@
+    for (int cnter=0; cnter<headerlist.size(); cnter++) {
+       mp_treeWidget->resizeColumnToContents(cnter);
+    }
+-
++   m_populating = false;
+ }
+ /*
+Index: src/qt-console/clients/clients.cpp
+===================================================================
+--- src/qt-console/clients/clients.cpp (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/clients/clients.cpp (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -51,6 +51,7 @@
+    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_client.h */
+    m_populated = false;
++   m_populating = false;
+    m_checkcurwidget = true;
+    m_closeable = false;
+    /* add context sensitive menu items specific to this classto the page
+@@ -70,6 +71,9 @@
+  */
+ void Clients::populateTree()
+ {
++   if (m_populating)
++      return;
++   m_populating = true;
+    QTreeWidgetItem *clientItem, *topItem;
+    if (!m_console->preventInUseConnect())
+@@ -135,6 +139,7 @@
+    for(int cnter=0; cnter<headerlist.size(); cnter++) {
+       mp_treeWidget->resizeColumnToContents(cnter);
+    }
++   m_populating = false;
+ }
+ /*
+Index: src/qt-console/clients/clients.h
+===================================================================
+--- src/qt-console/clients/clients.h   (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/clients/clients.h   (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -62,6 +62,7 @@
+    void createContextMenu();
+    QString m_currentlyselected;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkcurwidget;
+ };
+Index: src/qt-console/storage/storage.cpp
+===================================================================
+--- src/qt-console/storage/storage.cpp (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/storage/storage.cpp (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -52,6 +52,7 @@
+    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_storage.h */
+    m_populated = false;
++   m_populating = false;
+    m_checkcurwidget = true;
+    m_closeable = false;
+    m_currentStorage = "";
+@@ -71,6 +72,9 @@
+  */
+ void Storage::populateTree()
+ {
++   if (m_populating)
++      return;
++   m_populating = true;
+    QTreeWidgetItem *storageItem, *topItem;
+    if (!m_console->preventInUseConnect())
+@@ -135,6 +139,7 @@
+    for(int cnter=0; cnter<headerlist.size(); cnter++) {
+       mp_treeWidget->resizeColumnToContents(cnter);
+    }
++   m_populating = false;
+ }
+ /*
+Index: src/qt-console/storage/storage.h
+===================================================================
+--- src/qt-console/storage/storage.h   (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/storage/storage.h   (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -66,6 +66,7 @@
+    QString m_currentStorage;
+    int m_currentAutoChanger;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkcurwidget;
+ };
+Index: src/qt-console/medialist/medialist.h
+===================================================================
+--- src/qt-console/medialist/medialist.h       (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/medialist/medialist.h       (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -66,10 +66,13 @@
+ private:
+    void createContextMenu();
+    void setStatusColor(QTreeWidgetItem *, QString &, int &);
++   void writeExpandedSettings();
+    QString m_currentVolumeName;
+    QString m_currentVolumeId;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkcurwidget;
++   QTreeWidgetItem *m_topItem;
+ };
+ #endif /* _MEDIALIST_H_ */
+Index: src/qt-console/medialist/medialist.cpp
+===================================================================
+--- src/qt-console/medialist/medialist.cpp     (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/medialist/medialist.cpp     (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -54,6 +54,7 @@
+    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_medialist.h */
+    m_populated = false;
++   m_populating = false;
+    m_checkcurwidget = true;
+    m_closeable = false;
+    /* add context sensitive menu items specific to this classto the page
+@@ -64,6 +65,8 @@
+ MediaList::~MediaList()
+ {
++   if (m_populated)
++      writeExpandedSettings();
+ }
+ /*
+@@ -72,7 +75,10 @@
+  */
+ void MediaList::populateTree()
+ {
+-   QTreeWidgetItem *mediatreeitem, *pooltreeitem, *topItem;
++   QTreeWidgetItem *mediatreeitem, *pooltreeitem;
++   if (m_populating)
++      return;
++   m_populating = true;
+    if (!m_console->preventInUseConnect())
+        return;
+@@ -85,23 +91,31 @@
+    int statusIndex = headerlist.indexOf("Status");
+    m_checkcurwidget = false;
++   if (m_populated)
++      writeExpandedSettings();
+    mp_treeWidget->clear();
+    m_checkcurwidget = true;
+    mp_treeWidget->setColumnCount(headerlist.count());
+-   topItem = new QTreeWidgetItem(mp_treeWidget);
+-   topItem->setText(0, "Pools");
+-   topItem->setData(0, Qt::UserRole, 0);
+-   topItem->setExpanded(true);
++   m_topItem = new QTreeWidgetItem(mp_treeWidget);
++   m_topItem->setText(0, "Pools");
++   m_topItem->setData(0, Qt::UserRole, 0);
++   m_topItem->setExpanded(true);
+    
+    mp_treeWidget->setHeaderLabels(headerlist);
++   QSettings settings(m_console->m_dir->name(), "bat");
++   settings.beginGroup("MediaListTreeExpanded");
+    QString query;
+    foreach (QString pool_listItem, m_console->pool_list) {
+-      pooltreeitem = new QTreeWidgetItem(topItem);
++      pooltreeitem = new QTreeWidgetItem(m_topItem);
+       pooltreeitem->setText(0, pool_listItem);
+       pooltreeitem->setData(0, Qt::UserRole, 1);
+-      pooltreeitem->setExpanded(true);
++      if(settings.contains(pool_listItem)) {
++         pooltreeitem->setExpanded(settings.value(pool_listItem).toBool());
++      } else {
++         pooltreeitem->setExpanded(true);
++      }
+       query =  "SELECT Media.VolumeName AS Media, "
+          " Media.MediaId AS Id, Media.VolStatus AS VolStatus,"
+@@ -151,10 +165,12 @@
+          } /* foreach resultline */
+       } /* if results from query */
+    } /* foreach pool_listItem */
++   settings.endGroup();
+    /* Resize the columns */
+    for(int cnter=0; cnter<headerlist.count(); cnter++) {
+       mp_treeWidget->resizeColumnToContents(cnter);
+    }
++   m_populating = false;
+ }
+ void MediaList::setStatusColor(QTreeWidgetItem *item, QString &field, int &index)
+@@ -362,3 +378,18 @@
+    consoleCommand(cmd);
+    populateTree();
+ }
++
++/*
++ * Write settings to save expanded states of the pools
++ */
++void MediaList::writeExpandedSettings()
++{
++   QSettings settings(m_console->m_dir->name(), "bat");
++   settings.beginGroup("MediaListTreeExpanded");
++   int childcount = m_topItem->childCount();
++   for (int cnt=0; cnt<childcount; cnt++) {
++      QTreeWidgetItem *poolitem = m_topItem->child(cnt);
++      settings.setValue(poolitem->text(0), poolitem->isExpanded());
++   }
++   settings.endGroup();
++}
+Index: src/qt-console/mainwin.cpp
+===================================================================
+--- src/qt-console/mainwin.cpp (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/mainwin.cpp (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -441,14 +441,14 @@
+    new prerestorePage();
+ }
+-#ifdef HAVE_QWT
+ void MainWin::jobPlotButtonClicked()
+ {
++#ifdef HAVE_QWT
+    JobPlotPass pass;
+    pass.use = false;
+    new JobPlot(NULL, pass);
++#endif
+ }
+-#endif
+ /*
+  * The user just finished typing a line in the command line edit box
+Index: src/qt-console/jobs/jobs.h
+===================================================================
+--- src/qt-console/jobs/jobs.h (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/jobs/jobs.h (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -66,6 +66,7 @@
+    void createContextMenu();
+    QString m_currentlyselected;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkcurwidget;
+    int m_typeIndex;
+ };
+Index: src/qt-console/jobs/jobs.cpp
+===================================================================
+--- src/qt-console/jobs/jobs.cpp       (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/jobs/jobs.cpp       (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -49,6 +49,7 @@
+    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_client.h */
+    m_populated = false;
++   m_populating = false;
+    m_checkcurwidget = true;
+    m_closeable = false;
+    /* add context sensitive menu items specific to this classto the page
+@@ -68,6 +69,9 @@
+  */
+ void Jobs::populateTree()
+ {
++   if (m_populating)
++      return;
++   m_populating = true;
+    QTreeWidgetItem *jobsItem, *topItem;
+    if (!m_console->preventInUseConnect())
+Index: src/qt-console/joblist/joblist.cpp
+===================================================================
+--- src/qt-console/joblist/joblist.cpp (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/joblist/joblist.cpp (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -60,6 +60,7 @@
+    m_resultCount = 0;
+    m_populated = false;
++   m_populating = false;
+    m_closeable = false;
+    if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
+       m_closeable=true;
+@@ -106,6 +107,10 @@
+  */
+ void JobList::populateTable()
+ {
++   if (m_populating)
++      return;
++   m_populating = true;
++
+    QStringList results;
+    QString resultline;
+    QBrush blackBrush(Qt::black);
+@@ -293,6 +298,7 @@
+           tr("The Jobs query returned no results.\n"
+          "Press OK to continue?"), QMessageBox::Ok );
+    }
++   m_populating = false;
+ }
+ void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
+@@ -574,9 +580,9 @@
+ /*
+  * Graph this table
+  */
+-#ifdef HAVE_QWT
+ void JobList::graphTable()
+ {
++#ifdef HAVE_QWT
+    JobPlotPass pass;
+    pass.recordLimitCheck = limitCheckBox->checkState();
+    pass.daysLimitCheck = daysCheckBox->checkState();
+@@ -592,8 +598,8 @@
+    pass.use = true;
+    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
+    new JobPlot(pageSelectorTreeWidgetItem, pass);
++#endif
+ }
+-#endif
+ /*
+  * Save user settings associated with this page
+Index: src/qt-console/joblist/joblist.h
+===================================================================
+--- src/qt-console/joblist/joblist.h   (.../tags/Release-2.4.2/bacula) (revision 7468)
++++ src/qt-console/joblist/joblist.h   (.../branches/Branch-2.4/bacula)        (revision 7468)
+@@ -67,9 +67,7 @@
+    void preRestoreFromTime();
+    void showLogForJob();
+    void consoleCancelJob();
+-#ifdef HAVE_QWT
+    void graphTable();
+-#endif
+ private:
+    void createConnections();
+@@ -85,6 +83,7 @@
+    QString m_filesetName;
+    QString m_currentJob;
+    bool m_populated;
++   bool m_populating;
+    bool m_checkCurrentWidget;
+    int m_purgedIndex;
+    int m_typeIndex;
diff --git a/bacula/patches/2.4.2-estimate-cmd.patch b/bacula/patches/2.4.2-estimate-cmd.patch
new file mode 100644 (file)
index 0000000..ff98b54
--- /dev/null
@@ -0,0 +1,80 @@
+
+ This patch fixes the seg faults that occur in the Director if an incorrect
+ estimate command is given -- in particular a level specification without
+ the value.  This fixes bug #1140
+ Apply this patch to Bacula 2.4.2 (and possibly earlier versions) with:
+
+ cd <bacula-source>
+ patch -p0 <2.4.2-estimate-cmd.patch
+ ./configure <your-options>
+ make
+ ...
+ make install
+
+
+  
+Index: src/dird/ua_cmds.c
+===================================================================
+--- src/dird/ua_cmds.c (revision 7469)
++++ src/dird/ua_cmds.c (working copy)
+@@ -1079,17 +1079,31 @@
+           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
+          if (ua->argv[i]) {
+             client = GetClientResWithName(ua->argv[i]);
++            if (!client) {
++               ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
++               return 1;
++            }
+             continue;
++         } else {
++            ua->error_msg(_("Client name missing.\n"));
++            return 1;
+          }
+       }
+       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
+          if (ua->argv[i]) {
+             job = GetJobResWithName(ua->argv[i]);
+-            if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
++            if (!job) {
++               ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
++               return 1;
++            }
++            if (!acl_access_ok(ua, Job_ACL, job->name())) {
+                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
+                return 1;
+             }
+             continue;
++         } else {
++            ua->error_msg(_("Job name missing.\n"));
++            return 1;
+          }
+       }
+       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
+@@ -1100,15 +1114,25 @@
+                return 1;
+             }
+             continue;
++         } else {
++            ua->error_msg(_("Fileset name missing.\n"));
++            return 1;
+          }
++
+       }
+       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
+          listing = 1;
+          continue;
+       }
+       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
+-         if (!get_level_from_name(ua->jcr, ua->argv[i])) {
+-            ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
++         if (ua->argv[i]) {
++            if (!get_level_from_name(ua->jcr, ua->argv[i])) {
++               ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
++            }
++            continue;
++         } else {
++           ua->error_msg(_("Level value missing.\n"));
++           return 1;
+          }
+          continue;
+       }
index e5b7e2c35e966a3368bc30f2b31c6c4bbba48146..ce1218f04ad40bcdbbc4a512c1be754f50433323 100644 (file)
@@ -1324,6 +1324,47 @@ Item 1: Backup and Restore of Windows Encrypted Files through raw encryption fun
    verify a certain volume.  All of these would naturaly apply only for 
    Jobs whose file information are still in the catalog.
 
+Item X:   Add EFS support on Windows
+   Origin: Alex Ehrlich (Alex.Ehrlich-at-mail.ee)
+   Date:   05 August 2008
+   Status:
+
+   What:   For each file backed up or restored by FD on Windows, check if
+           the file is encrypted; if so then use OpenEncryptedFileRaw,
+           ReadEncryptedFileRaw, WriteEncryptedFileRaw,
+           CloseEncryptedFileRaw instead of BackupRead and BackupWrite
+           API calls.
+
+   Why:    Many laptop users utilize the EFS functionality today; so do.
+           some non-laptop ones, too.
+           Currently files encrypted by means of EFS cannot be backed up.
+           It means a Windows boutique cannot rely on Bacula as its
+           backup solution, at least when using Windows 2K, XPP,
+           "better" Vista etc on workstations, unless EFS is
+           forbidden by policies.
+           The current situation might result into "false sense of
+           security" among the end-users.
+
+   Notes:  Using xxxEncryptedFileRaw API would allow to backup and
+           restore EFS-encrypted files without decrypting their data.
+           Note that such files cannot be restored "portably" (at least,
+           easily) but they would be restoreable to a different (or
+           reinstalled) Win32 machine; the restore would require setup
+           of a EFS recovery agent in advance, of course, and this shall
+           be clearly reflected in the documentation, but this is the
+           normal Windows SysAdmin's business.
+           When "portable" backup is requested the EFS-encrypted files
+           shall be clearly reported as errors.
+           See MSDN on the "Backup and Restore of Encrypted Files" topic:
+           http://msdn.microsoft.com/en-us/library/aa363783.aspx
+           Maybe the EFS support requires a new flag in the database for
+           each file, too?
+           Unfortunately, the implementation is not as straightforward as
+           1-to-1 replacement of BackupRead with ReadEncryptedFileRaw,
+           requiring some FD code rewrite to work with
+           encrypted-file-related callback functions.
+
+                                                                                                                                                                                              encrypted-file-related callback functions.
 ========== Already implemented ================================
 
 Item  n:  make changing "spooldata=yes|no" possible for
index 208d9095e8424b0b16f045437f9daf70333aa289..ac4b89fc77bf5550e5c0fa5f66339499400ed210 100644 (file)
@@ -1084,6 +1084,9 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
                return 1;
             }
             continue;
+         } else {
+            ua->error_msg(_("Client name missing.\n"));
+            return 1;
          }
       }
       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
@@ -1098,7 +1101,11 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
                return 1;
             }
             continue;
+         } else {
+            ua->error_msg(_("Job name missing.\n"));
+            return 1;
          }
+
       }
       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
          if (ua->argv[i]) {
@@ -1112,6 +1119,9 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
                return 1;
             }
             continue;
+         } else {
+            ua->error_msg(_("Fileset name missing.\n"));
+            return 1;
          }
       }
       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
@@ -1119,10 +1129,15 @@ static int estimate_cmd(UAContext *ua, const char *cmd)
          continue;
       }
       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
-         if (!get_level_from_name(ua->jcr, ua->argv[i])) {
-            ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
+         if (ua->argv[i]) {
+            if (!get_level_from_name(ua->jcr, ua->argv[i])) {
+               ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
+            }
+            continue;
+         } else {
+           ua->error_msg(_("Level value missing.\n"));
+           return 1;
          }
-         continue;
       }
    }
    if (!job && !(client && fileset)) {
index e65f12ae8775b91d1f15c11ee26fd2124e00a43e..24c02cccde254a697c170fd37b2a50d64f46a06e 100644 (file)
@@ -4,8 +4,8 @@
 
 #undef  VERSION
 #define VERSION "2.5.3"
-#define BDATE   "08 Aug 2008"
-#define LSMDATE "08Aug08"
+#define BDATE   "12 Aug 2008"
+#define LSMDATE "12Aug08"
 
 #define PROG_COPYRIGHT "Copyright (C) %d-2008 Free Software Foundation Europe e.V.\n"
 #define BYEAR "2008"       /* year for copyright messages in progs */
index 6c355d22e520a63cae03d011c287061a084945b1..a7453becf0dc24c09ba39370ec46429745623e66 100644 (file)
@@ -32,6 +32,9 @@ separator in console (!$%&'()*+,-/:;<>?[]^`{|}~)
 
 
 General:
+12Aug08 
+kes  Fix seg fault in Dir during estimate command with no level value
+     given. This fixes bug #1140.
 08Aug08
 kes  Add message to migration job when the target job is already migrated.
      This closes bug #1129.