]> git.sur5r.net Git - bacula/bacula/commitdiff
ebl Update copy patch
authorEric Bollengier <eric@eb.homelinux.org>
Sat, 20 Dec 2008 21:17:54 +0000 (21:17 +0000)
committerEric Bollengier <eric@eb.homelinux.org>
Sat, 20 Dec 2008 21:17:54 +0000 (21:17 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@8207 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/patches/testing/copy_list_copies_cmd.patch

index 2a7fb19d4bd4da7ade2da955f5c785b4dbd13a0d..e7a5c81f5c9d012980b5e0b9a1560c1474d69f4b 100644 (file)
@@ -1,26 +1,38 @@
 Index: src/dird/ua_output.c
 ===================================================================
---- src/dird/ua_output.c       (revision 8163)
+--- src/dird/ua_output.c       (revision 8203)
 +++ src/dird/ua_output.c       (working copy)
-@@ -454,6 +454,15 @@
+@@ -223,6 +223,7 @@
+  *  list clients        - list clients
+  *  list nextvol job=xx  - list the next vol to be used by job
+  *  list nextvolume job=xx - same as above.
++ *  list copies jobid=x,y,z
+  *
+  */
+@@ -454,6 +455,19 @@
              }
           }
           list_nextvol(ua, n);
 +      } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
++         char *jobids=NULL;
++         uint32_t limit=0;
 +         for (j=i+1; j<ua->argc; j++) {
 +            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
-+               jr.JobId = str_to_int64(ua->argv[j]);
++               if (is_a_number_list(ua->argv[j])) {
++                  jobids = ua->argv[j];
++               }
 +            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
-+               jr.limit = atoi(ua->argv[j]);
++               limit = atoi(ua->argv[j]);
 +            } 
 +         }
-+         db_list_copies_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
++         db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
        } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
                   || strcasecmp(ua->argk[i], NT_("days")) == 0) {
           /* Ignore it */
 Index: src/dird/migrate.c
 ===================================================================
---- src/dird/migrate.c (revision 8179)
+--- src/dird/migrate.c (revision 8203)
 +++ src/dird/migrate.c (working copy)
 @@ -1158,13 +1158,17 @@
        /*
@@ -41,9 +53,183 @@ Index: src/dird/migrate.c
        } 
  
        if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
+Index: src/dird/ua_purge.c
+===================================================================
+--- src/dird/ua_purge.c        (revision 8203)
++++ src/dird/ua_purge.c        (working copy)
+@@ -360,6 +360,57 @@
+ }
+ /*
++ * Change the type of the next copy job to backup.
++ * We need to upgrade the next copy of a normal job,
++ * and also upgrade the next copy when the normal job
++ * already have been purged.
++ *
++ *   JobId: 1   PriorJobId: 0    (original)
++ *   JobId: 2   PriorJobId: 1    (first copy)
++ *   JobId: 3   PriorJobId: 1    (second copy)
++ *
++ *   JobId: 2   PriorJobId: 1    (first copy, now regular backup)
++ *   JobId: 3   PriorJobId: 1    (second copy)
++ *
++ *  => Search through PriorJobId in jobid and
++ *                    PriorJobId in PriorJobId (jobid)
++ */
++void upgrade_copies(UAContext *ua, char *jobs)
++{
++   POOL_MEM query(PM_MESSAGE);
++   
++   db_lock(ua->db);
++   /* Do it in two times for mysql */
++   Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS "
++                  "SELECT MIN(JobId) AS JobId FROM Job "     /* Choose the oldest job */
++                   "WHERE Type='%c' "
++                     "AND ( PriorJobId IN (%s) "
++                         "OR "
++                          " PriorJobId IN ( "
++                             "SELECT PriorJobId "
++                               "FROM Job "
++                              "WHERE JobId IN (%s) "
++                               " AND Type='B' "
++                            ") "
++                         ") "
++                   "GROUP BY PriorJobId ",           /* one result per copy */
++        JT_JOB_COPY, jobs, jobs);
++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
++
++   /* Now upgrade first copy to Backup */
++   Mmsg(query, "UPDATE Job SET Type='B' "           /* JT_JOB_COPY => JT_BACKUP  */
++                "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )");
++
++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
++
++   Mmsg(query, "DROP TABLE cpy_tmp");
++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
++
++   db_unlock(ua->db);
++   Dmsg1(00, "Upgrade copies Log sql=%s\n", query.c_str());
++}
++
++/*
+  * Remove all records from catalog for a list of JobIds
+  */
+ void purge_jobs_from_catalog(UAContext *ua, char *jobs)
+@@ -377,13 +428,15 @@
+    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+    Dmsg1(050, "Delete Log sql=%s\n", query.c_str());
++   upgrade_copies(ua, jobs);
++
+    /* Now remove the Job record itself */
+    Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs);
+    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
++
+    Dmsg1(050, "Delete Job sql=%s\n", query.c_str());
+ }
+-
+ void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr )
+ {} /* ***FIXME*** implement */
+Index: src/dird/ua_restore.c
+===================================================================
+--- src/dird/ua_restore.c      (revision 8203)
++++ src/dird/ua_restore.c      (working copy)
+@@ -444,6 +444,7 @@
+       "add_suffix",   /* 17 */
+       "regexwhere",   /* 18 */
+       "restoreclient", /* 19 */
++      "copies",        /* 20 */
+       NULL
+    };
+@@ -1138,9 +1139,10 @@
+    bool ok = false;
+    FILESET_DBR fsr;
+    CLIENT_DBR cr;
++   POOL_MEM other_filter(PM_MESSAGE);
++   POOL_MEM temp_filter(PM_MESSAGE);
+    char fileset_name[MAX_NAME_LENGTH];
+    char ed1[50], ed2[50];
+-   char pool_select[MAX_NAME_LENGTH];
+    int i;
+    /* Create temp tables */
+@@ -1196,23 +1198,32 @@
+    }
+    /* If Pool specified, add PoolId specification */
+-   pool_select[0] = 0;
+    if (rx->pool) {
+       POOL_DBR pr;
+       memset(&pr, 0, sizeof(pr));
+       bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
+       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
+-         bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
+-            edit_int64(pr.PoolId, ed1));
++         Mmsg(other_filter, " AND Media.PoolId=%s ", 
++              edit_int64(pr.PoolId, ed1));
+       } else {
+          ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
+       }
+    }
++   /* include copies or not in job selection */
++   if (find_arg(ua, NT_("copies")) > 0) {
++      Mmsg(temp_filter, "%s AND Job.Type IN ('%c', '%c') ", 
++           other_filter.c_str(), (char)JT_BACKUP, (char)JT_JOB_COPY);
++   } else {
++      Mmsg(temp_filter, "%s AND Job.Type = '%c' ", other_filter.c_str(),
++           (char)JT_BACKUP);
++   }
++   pm_strcpy(other_filter, temp_filter.c_str());
+    /* Find JobId of last Full backup for this client, fileset */
+    edit_int64(cr.ClientId, ed1);
+    Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
+-         pool_select);
++        other_filter.c_str());
++   Dmsg1(0, "sql=%s\n", rx->query);
+    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
+       ua->error_msg("%s\n", db_strerror(ua->db));
+       goto bail_out;
+@@ -1238,12 +1249,13 @@
+    /* Now find most recent Differental Job after Full save, if any */
+    Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
+-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
+    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
+       ua->warning_msg("%s\n", db_strerror(ua->db));
+    }
+    /* Now update JobTDate to lock onto Differental, if any */
+    rx->JobTDate = 0;
++   Dmsg1(0, "sql=%s\n", rx->query);
+    if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
+       ua->warning_msg("%s\n", db_strerror(ua->db));
+    }
+@@ -1254,7 +1266,8 @@
+    /* Now find all Incremental Jobs after Full/dif save */
+    Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
+-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
++   Dmsg1(0, "sql=%s\n", rx->query);
+    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
+       ua->warning_msg("%s\n", db_strerror(ua->db));
+    }
+@@ -1267,6 +1280,8 @@
+    }
+    if (rx->JobIds[0] != 0) {
++      /* Display a list of all copies */
++      db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, prtit, ua, HORZ_LIST);
+       /* Display a list of Jobs selected for this restore */
+       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
+       ok = true;
 Index: src/dird/ua_cmds.c
 ===================================================================
---- src/dird/ua_cmds.c (revision 8163)
+--- src/dird/ua_cmds.c (revision 8203)
 +++ src/dird/ua_cmds.c (working copy)
 @@ -123,7 +123,7 @@
   { NT_("exit"),       quit_cmd,      _("exit = quit"),                                false},
@@ -56,48 +242,101 @@ Index: src/dird/ua_cmds.c
   { NT_("messages"),   messagescmd,   _("messages"),                                   false},
 Index: src/cats/protos.h
 ===================================================================
---- src/cats/protos.h  (revision 8163)
+--- src/cats/protos.h  (revision 8203)
 +++ src/cats/protos.h  (working copy)
 @@ -124,6 +124,7 @@
  void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
  int  db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit, void *ctx, int verbose, e_list_type type);
  void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
-+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
++void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *jobids, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
  
  /* sql_update.c */
  bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
+Index: src/cats/sql_cmds.c
+===================================================================
+--- src/cats/sql_cmds.c        (revision 8203)
++++ src/cats/sql_cmds.c        (working copy)
+@@ -286,7 +286,7 @@
+    "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s "
+    "AND Job.ClientId=%s "
+    "AND Job.StartTime<'%s' "
+-   "AND Level='F' AND JobStatus='T' AND Type='B' "
++   "AND Level='F' AND JobStatus='T' "
+    "AND JobMedia.JobId=Job.JobId "
+    "AND Media.Enabled=1 "
+    "AND JobMedia.MediaId=Media.MediaId "
+@@ -297,13 +297,14 @@
+ const char *uar_full =
+    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,"
+-   "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
+-   "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
+-   "FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId "
+-   "AND Level='F' AND JobStatus='T' AND Type='B' "
+-   "AND Media.Enabled=1 "
+-   "AND JobMedia.JobId=Job.JobId "
+-   "AND JobMedia.MediaId=Media.MediaId";
++     "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
++     "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
++    "FROM temp1,Job,JobMedia,Media "
++   "WHERE temp1.JobId=Job.JobId "
++     "AND Level='F' AND JobStatus='T' "
++     "AND Media.Enabled=1 "
++     "AND JobMedia.JobId=Job.JobId "
++     "AND JobMedia.MediaId=Media.MediaId";
+ const char *uar_dif =
+    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
+@@ -316,7 +317,7 @@
+    "AND JobMedia.JobId=Job.JobId "
+    "AND Media.Enabled=1 "
+    "AND JobMedia.MediaId=Media.MediaId "
+-   "AND Job.Level='D' AND JobStatus='T' AND Type='B' "
++   "AND Job.Level='D' AND JobStatus='T' "
+    "AND Job.FileSetId=FileSet.FileSetId "
+    "AND FileSet.FileSet='%s' "
+    "%s"
+@@ -333,7 +334,7 @@
+    "AND Media.Enabled=1 "
+    "AND JobMedia.JobId=Job.JobId "
+    "AND JobMedia.MediaId=Media.MediaId "
+-   "AND Job.Level='I' AND JobStatus='T' AND Type='B' "
++   "AND Job.Level='I' AND JobStatus='T' "
+    "AND Job.FileSetId=FileSet.FileSetId "
+    "AND FileSet.FileSet='%s' "
+    "%s";
 Index: src/cats/sql_list.c
 ===================================================================
---- src/cats/sql_list.c        (revision 8163)
+--- src/cats/sql_list.c        (revision 8203)
 +++ src/cats/sql_list.c        (working copy)
 @@ -242,6 +242,43 @@
  }
  
  
-+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr,
++void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
 +                            DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 +{
-+   char ed1[50];
-+   POOL_MEM limit(PM_MESSAGE);
-+   POOL_MEM jobids(PM_MESSAGE);
++   POOL_MEM str_limit(PM_MESSAGE);
++   POOL_MEM str_jobids(PM_MESSAGE);
 +
-+   if (jr->limit > 0) {
-+      Mmsg(limit, " LIMIT %d", jr->limit);
++   if (limit > 0) {
++      Mmsg(str_limit, " LIMIT %d", limit);
 +   }
 +
-+   if (jr->JobId) {
-+      Mmsg(jobids, " AND (C.PriorJobId = %s OR C.JobId = %s) ", 
-+           edit_int64(jr->JobId, ed1),ed1);      
++   if (JobIds && JobIds[0]) {
++      Mmsg(str_jobids, " AND (C.PriorJobId IN (%s) OR C.JobId IN (%s)) ", 
++           JobIds, JobIds);      
 +   }
 +
 +   db_lock(mdb);
 +   Mmsg(mdb->cmd, 
-+   "SELECT C.PriorJobId AS JobId, C.Job, C.JobId AS CopyJobId, M.MediaType "
++   "SELECT DISTINCT C.PriorJobId AS JobId, C.Job, "
++                   "C.JobId AS CopyJobId, M.MediaType "
 +     "FROM Job AS C " 
 +     "JOIN JobMedia    USING (JobId) "
 +     "JOIN Media AS M  USING (MediaId) "
 +    "WHERE C.Type = '%c' %s ORDER BY C.PriorJobId DESC %s",
-+        (char) JT_JOB_COPY, jobids.c_str(), limit.c_str());
++        (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
 +
 +   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
 +      goto bail_out;
@@ -116,7 +355,7 @@ Index: src/cats/sql_list.c
  {
 Index: src/jcr.h
 ===================================================================
---- src/jcr.h  (revision 8163)
+--- src/jcr.h  (revision 8203)
 +++ src/jcr.h  (working copy)
 @@ -60,11 +60,12 @@
  #define JT_MIGRATED_JOB          'M'  /* A previous backup job that was migrated */
@@ -135,7 +374,7 @@ Index: src/jcr.h
  
 Index: src/lib/util.c
 ===================================================================
---- src/lib/util.c     (revision 8163)
+--- src/lib/util.c     (revision 8203)
 +++ src/lib/util.c     (working copy)
 @@ -361,6 +361,9 @@
     case JT_COPY:
@@ -147,3 +386,589 @@ Index: src/lib/util.c
     case JT_CONSOLE:
        str = _("Console");
        break;
+Index: src/lib/protos.h
+===================================================================
+--- src/lib/protos.h   (revision 8203)
++++ src/lib/protos.h   (working copy)
+@@ -186,6 +186,7 @@
+ bool             size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
+ char             *edit_utime             (utime_t val, char *buf, int buf_len);
+ bool             is_a_number             (const char *num);
++bool             is_a_number_list        (const char *n);
+ bool             is_an_integer           (const char *n);
+ bool             is_name_valid           (char *name, POOLMEM **msg);
+Index: src/lib/edit.c
+===================================================================
+--- src/lib/edit.c     (revision 8203)
++++ src/lib/edit.c     (working copy)
+@@ -407,6 +407,27 @@
+ }
+ /*
++ * Check if specified string is a list of number or not
++ */
++bool is_a_number_list(const char *n)
++{
++   bool previous_digit = false; 
++   bool digit_seen = false;
++   while (*n) {
++      if (B_ISDIGIT(*n)) {
++         previous_digit=true;
++         digit_seen = true;
++      } else if (*n == ',' && previous_digit) {
++         previous_digit = false;
++      } else {
++         return false;
++      }
++      n++;
++   }
++   return digit_seen && *n==0; 
++}
++
++/*
+  * Check if the specified string is an integer
+  */
+ bool is_an_integer(const char *n)
+Index: patches/testing/copy_list_copies_cmd.patch
+===================================================================
+--- patches/testing/copy_list_copies_cmd.patch (revision 8203)
++++ patches/testing/copy_list_copies_cmd.patch (working copy)
+@@ -1,26 +1,38 @@
+ Index: src/dird/ua_output.c
+ ===================================================================
+---- src/dird/ua_output.c      (revision 8163)
++--- src/dird/ua_output.c      (revision 8203)
+ +++ src/dird/ua_output.c      (working copy)
+-@@ -454,6 +454,15 @@
++@@ -223,6 +223,7 @@
++  *  list clients        - list clients
++  *  list nextvol job=xx  - list the next vol to be used by job
++  *  list nextvolume job=xx - same as above.
+++ *  list copies jobid=x,y,z
++  *
++  */
++ 
++@@ -454,6 +455,19 @@
+              }
+           }
+           list_nextvol(ua, n);
+ +      } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
+++         char *jobids=NULL;
+++         uint32_t limit=0;
+ +         for (j=i+1; j<ua->argc; j++) {
+ +            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
+-+               jr.JobId = str_to_int64(ua->argv[j]);
+++               if (is_a_number_list(ua->argv[j])) {
+++                  jobids = ua->argv[j];
+++               }
+ +            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
+-+               jr.limit = atoi(ua->argv[j]);
+++               limit = atoi(ua->argv[j]);
+ +            } 
+ +         }
+-+         db_list_copies_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
+++         db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
+        } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
+                   || strcasecmp(ua->argk[i], NT_("days")) == 0) {
+           /* Ignore it */
+ Index: src/dird/migrate.c
+ ===================================================================
+---- src/dird/migrate.c        (revision 8179)
++--- src/dird/migrate.c        (revision 8203)
+ +++ src/dird/migrate.c        (working copy)
+ @@ -1158,13 +1158,17 @@
+        /*
+@@ -41,9 +53,183 @@
+        } 
+  
+        if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
++Index: src/dird/ua_purge.c
++===================================================================
++--- src/dird/ua_purge.c       (revision 8203)
+++++ src/dird/ua_purge.c       (working copy)
++@@ -360,6 +360,57 @@
++ }
++ 
++ /*
+++ * Change the type of the next copy job to backup.
+++ * We need to upgrade the next copy of a normal job,
+++ * and also upgrade the next copy when the normal job
+++ * already have been purged.
+++ *
+++ *   JobId: 1   PriorJobId: 0    (original)
+++ *   JobId: 2   PriorJobId: 1    (first copy)
+++ *   JobId: 3   PriorJobId: 1    (second copy)
+++ *
+++ *   JobId: 2   PriorJobId: 1    (first copy, now regular backup)
+++ *   JobId: 3   PriorJobId: 1    (second copy)
+++ *
+++ *  => Search through PriorJobId in jobid and
+++ *                    PriorJobId in PriorJobId (jobid)
+++ */
+++void upgrade_copies(UAContext *ua, char *jobs)
+++{
+++   POOL_MEM query(PM_MESSAGE);
+++   
+++   db_lock(ua->db);
+++   /* Do it in two times for mysql */
+++   Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS "
+++                  "SELECT MIN(JobId) AS JobId FROM Job "     /* Choose the oldest job */
+++                   "WHERE Type='%c' "
+++                     "AND ( PriorJobId IN (%s) "
+++                         "OR "
+++                          " PriorJobId IN ( "
+++                             "SELECT PriorJobId "
+++                               "FROM Job "
+++                              "WHERE JobId IN (%s) "
+++                               " AND Type='B' "
+++                            ") "
+++                         ") "
+++                   "GROUP BY PriorJobId ",           /* one result per copy */
+++        JT_JOB_COPY, jobs, jobs);
+++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+++
+++   /* Now upgrade first copy to Backup */
+++   Mmsg(query, "UPDATE Job SET Type='B' "           /* JT_JOB_COPY => JT_BACKUP  */
+++                "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )");
+++
+++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+++
+++   Mmsg(query, "DROP TABLE cpy_tmp");
+++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+++
+++   db_unlock(ua->db);
+++   Dmsg1(00, "Upgrade copies Log sql=%s\n", query.c_str());
+++}
+++
+++/*
++  * Remove all records from catalog for a list of JobIds
++  */
++ void purge_jobs_from_catalog(UAContext *ua, char *jobs)
++@@ -377,13 +428,15 @@
++    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
++    Dmsg1(050, "Delete Log sql=%s\n", query.c_str());
++ 
+++   upgrade_copies(ua, jobs);
+++
++    /* Now remove the Job record itself */
++    Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs);
++    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
+++
++    Dmsg1(050, "Delete Job sql=%s\n", query.c_str());
++ }
++ 
++-
++ void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr )
++ {} /* ***FIXME*** implement */
++ 
++Index: src/dird/ua_restore.c
++===================================================================
++--- src/dird/ua_restore.c     (revision 8203)
+++++ src/dird/ua_restore.c     (working copy)
++@@ -444,6 +444,7 @@
++       "add_suffix",   /* 17 */
++       "regexwhere",   /* 18 */
++       "restoreclient", /* 19 */
+++      "copies",        /* 20 */
++       NULL
++    };
++ 
++@@ -1138,9 +1139,10 @@
++    bool ok = false;
++    FILESET_DBR fsr;
++    CLIENT_DBR cr;
+++   POOL_MEM other_filter(PM_MESSAGE);
+++   POOL_MEM temp_filter(PM_MESSAGE);
++    char fileset_name[MAX_NAME_LENGTH];
++    char ed1[50], ed2[50];
++-   char pool_select[MAX_NAME_LENGTH];
++    int i;
++ 
++    /* Create temp tables */
++@@ -1196,23 +1198,32 @@
++    }
++ 
++    /* If Pool specified, add PoolId specification */
++-   pool_select[0] = 0;
++    if (rx->pool) {
++       POOL_DBR pr;
++       memset(&pr, 0, sizeof(pr));
++       bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
++       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
++-         bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
++-            edit_int64(pr.PoolId, ed1));
+++         Mmsg(other_filter, " AND Media.PoolId=%s ", 
+++              edit_int64(pr.PoolId, ed1));
++       } else {
++          ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
++       }
++    }
+++   /* include copies or not in job selection */
+++   if (find_arg(ua, NT_("copies")) > 0) {
+++      Mmsg(temp_filter, "%s AND Job.Type IN ('%c', '%c') ", 
+++           other_filter.c_str(), (char)JT_BACKUP, (char)JT_JOB_COPY);
+++   } else {
+++      Mmsg(temp_filter, "%s AND Job.Type = '%c' ", other_filter.c_str(),
+++           (char)JT_BACKUP);
+++   }
+++   pm_strcpy(other_filter, temp_filter.c_str());
++ 
++    /* Find JobId of last Full backup for this client, fileset */
++    edit_int64(cr.ClientId, ed1);
++    Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
++-         pool_select);
+++        other_filter.c_str());
+++   Dmsg1(0, "sql=%s\n", rx->query);
++    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
++       ua->error_msg("%s\n", db_strerror(ua->db));
++       goto bail_out;
++@@ -1238,12 +1249,13 @@
++ 
++    /* Now find most recent Differental Job after Full save, if any */
++    Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
++-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
+++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
++    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
++       ua->warning_msg("%s\n", db_strerror(ua->db));
++    }
++    /* Now update JobTDate to lock onto Differental, if any */
++    rx->JobTDate = 0;
+++   Dmsg1(0, "sql=%s\n", rx->query);
++    if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
++       ua->warning_msg("%s\n", db_strerror(ua->db));
++    }
++@@ -1254,7 +1266,8 @@
++ 
++    /* Now find all Incremental Jobs after Full/dif save */
++    Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
++-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
+++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
+++   Dmsg1(0, "sql=%s\n", rx->query);
++    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
++       ua->warning_msg("%s\n", db_strerror(ua->db));
++    }
++@@ -1267,6 +1280,8 @@
++    }
++ 
++    if (rx->JobIds[0] != 0) {
+++      /* Display a list of all copies */
+++      db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, prtit, ua, HORZ_LIST);
++       /* Display a list of Jobs selected for this restore */
++       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
++       ok = true;
+ Index: src/dird/ua_cmds.c
+ ===================================================================
+---- src/dird/ua_cmds.c        (revision 8163)
++--- src/dird/ua_cmds.c        (revision 8203)
+ +++ src/dird/ua_cmds.c        (working copy)
+ @@ -123,7 +123,7 @@
+   { NT_("exit"),       quit_cmd,      _("exit = quit"),                                false},
+@@ -56,48 +242,101 @@
+   { NT_("messages"),   messagescmd,   _("messages"),                                   false},
+ Index: src/cats/protos.h
+ ===================================================================
+---- src/cats/protos.h (revision 8163)
++--- src/cats/protos.h (revision 8203)
+ +++ src/cats/protos.h (working copy)
+ @@ -124,6 +124,7 @@
+  void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+  int  db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit, void *ctx, int verbose, e_list_type type);
+  void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+-+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+++void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *jobids, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
+  
+  /* sql_update.c */
+  bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
++Index: src/cats/sql_cmds.c
++===================================================================
++--- src/cats/sql_cmds.c       (revision 8203)
+++++ src/cats/sql_cmds.c       (working copy)
++@@ -286,7 +286,7 @@
++    "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s "
++    "AND Job.ClientId=%s "
++    "AND Job.StartTime<'%s' "
++-   "AND Level='F' AND JobStatus='T' AND Type='B' "
+++   "AND Level='F' AND JobStatus='T' "
++    "AND JobMedia.JobId=Job.JobId "
++    "AND Media.Enabled=1 "
++    "AND JobMedia.MediaId=Media.MediaId "
++@@ -297,13 +297,14 @@
++ 
++ const char *uar_full =
++    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,"
++-   "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
++-   "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
++-   "FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId "
++-   "AND Level='F' AND JobStatus='T' AND Type='B' "
++-   "AND Media.Enabled=1 "
++-   "AND JobMedia.JobId=Job.JobId "
++-   "AND JobMedia.MediaId=Media.MediaId";
+++     "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
+++     "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
+++    "FROM temp1,Job,JobMedia,Media "
+++   "WHERE temp1.JobId=Job.JobId "
+++     "AND Level='F' AND JobStatus='T' "
+++     "AND Media.Enabled=1 "
+++     "AND JobMedia.JobId=Job.JobId "
+++     "AND JobMedia.MediaId=Media.MediaId";
++ 
++ const char *uar_dif =
++    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
++@@ -316,7 +317,7 @@
++    "AND JobMedia.JobId=Job.JobId "
++    "AND Media.Enabled=1 "
++    "AND JobMedia.MediaId=Media.MediaId "
++-   "AND Job.Level='D' AND JobStatus='T' AND Type='B' "
+++   "AND Job.Level='D' AND JobStatus='T' "
++    "AND Job.FileSetId=FileSet.FileSetId "
++    "AND FileSet.FileSet='%s' "
++    "%s"
++@@ -333,7 +334,7 @@
++    "AND Media.Enabled=1 "
++    "AND JobMedia.JobId=Job.JobId "
++    "AND JobMedia.MediaId=Media.MediaId "
++-   "AND Job.Level='I' AND JobStatus='T' AND Type='B' "
+++   "AND Job.Level='I' AND JobStatus='T' "
++    "AND Job.FileSetId=FileSet.FileSetId "
++    "AND FileSet.FileSet='%s' "
++    "%s";
+ Index: src/cats/sql_list.c
+ ===================================================================
+---- src/cats/sql_list.c       (revision 8163)
++--- src/cats/sql_list.c       (revision 8203)
+ +++ src/cats/sql_list.c       (working copy)
+ @@ -242,6 +242,43 @@
+  }
+  
+  
+-+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr,
+++void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
+ +                            DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
+ +{
+-+   char ed1[50];
+-+   POOL_MEM limit(PM_MESSAGE);
+-+   POOL_MEM jobids(PM_MESSAGE);
+++   POOL_MEM str_limit(PM_MESSAGE);
+++   POOL_MEM str_jobids(PM_MESSAGE);
+ +
+-+   if (jr->limit > 0) {
+-+      Mmsg(limit, " LIMIT %d", jr->limit);
+++   if (limit > 0) {
+++      Mmsg(str_limit, " LIMIT %d", limit);
+ +   }
+ +
+-+   if (jr->JobId) {
+-+      Mmsg(jobids, " AND (C.PriorJobId = %s OR C.JobId = %s) ", 
+-+           edit_int64(jr->JobId, ed1),ed1);      
+++   if (JobIds && JobIds[0]) {
+++      Mmsg(str_jobids, " AND (C.PriorJobId IN (%s) OR C.JobId IN (%s)) ", 
+++           JobIds, JobIds);      
+ +   }
+ +
+ +   db_lock(mdb);
+ +   Mmsg(mdb->cmd, 
+-+   "SELECT C.PriorJobId AS JobId, C.Job, C.JobId AS CopyJobId, M.MediaType "
+++   "SELECT DISTINCT C.PriorJobId AS JobId, C.Job, "
+++                   "C.JobId AS CopyJobId, M.MediaType "
+ +     "FROM Job AS C " 
+ +     "JOIN JobMedia    USING (JobId) "
+ +     "JOIN Media AS M  USING (MediaId) "
+ +    "WHERE C.Type = '%c' %s ORDER BY C.PriorJobId DESC %s",
+-+        (char) JT_JOB_COPY, jobids.c_str(), limit.c_str());
+++        (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
+ +
+ +   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+ +      goto bail_out;
+@@ -116,7 +355,7 @@
+  {
+ Index: src/jcr.h
+ ===================================================================
+---- src/jcr.h (revision 8163)
++--- src/jcr.h (revision 8203)
+ +++ src/jcr.h (working copy)
+ @@ -60,11 +60,12 @@
+  #define JT_MIGRATED_JOB          'M'  /* A previous backup job that was migrated */
+@@ -135,7 +374,7 @@
+  
+ Index: src/lib/util.c
+ ===================================================================
+---- src/lib/util.c    (revision 8163)
++--- src/lib/util.c    (revision 8203)
+ +++ src/lib/util.c    (working copy)
+ @@ -361,6 +361,9 @@
+     case JT_COPY:
+@@ -147,3 +386,47 @@
+     case JT_CONSOLE:
+        str = _("Console");
+        break;
++Index: src/lib/protos.h
++===================================================================
++--- src/lib/protos.h  (revision 8203)
+++++ src/lib/protos.h  (working copy)
++@@ -186,6 +186,7 @@
++ bool             size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
++ char             *edit_utime             (utime_t val, char *buf, int buf_len);
++ bool             is_a_number             (const char *num);
+++bool             is_a_number_list        (const char *n);
++ bool             is_an_integer           (const char *n);
++ bool             is_name_valid           (char *name, POOLMEM **msg);
++ 
++Index: src/lib/edit.c
++===================================================================
++--- src/lib/edit.c    (revision 8203)
+++++ src/lib/edit.c    (working copy)
++@@ -407,6 +407,27 @@
++ }
++ 
++ /*
+++ * Check if specified string is a list of number or not
+++ */
+++bool is_a_number_list(const char *n)
+++{
+++   bool previous_digit = false; 
+++   bool digit_seen = false;
+++   while (*n) {
+++      if (B_ISDIGIT(*n)) {
+++         previous_digit=true;
+++         digit_seen = true;
+++      } else if (*n == ',' && previous_digit) {
+++         previous_digit = false;
+++      } else {
+++         return false;
+++      }
+++      n++;
+++   }
+++   return digit_seen && *n==0; 
+++}
+++
+++/*
++  * Check if the specified string is an integer
++  */
++ bool is_an_integer(const char *n)
+Index: patches/testing/fix_1190.patch
+===================================================================
+--- patches/testing/fix_1190.patch     (revision 8203)
++++ patches/testing/fix_1190.patch     (working copy)
+@@ -201,7 +201,6 @@
+     }
+ -//  Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
+ -//             volblock->sblock, volblock->eblock, rec->Block);
+--   if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
+ +   Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
+ +         volblock->sblock, volblock->eblock, rec->Block);
+ +
+@@ -213,8 +212,8 @@
+ +    * But, we are already decoding rec->Block-1Block records
+ +    */
+ +   uint32_t max = volblock->eblock+DEFAULT_BLOCK_SIZE;
+-+//   if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
+-+   if (min <= rec->Block && max >= rec->Block) {
++    if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
+++//   if (min <= rec->Block && max >= rec->Block) {
+        return 1;
+     }
+     /* Once we get past last eblock, we are done */
+@@ -288,3 +287,23 @@
+     }
+     /*
+      * Check for Start or End of Session Record
++Index: block.c
++===================================================================
++--- block.c   (rĂ©vision 8116)
+++++ block.c   (copie de travail)
++@@ -1116,11 +1116,12 @@
++       dcr->EndBlock = dev->EndBlock;
++       dcr->EndFile  = dev->EndFile;
++    } else {
++-      uint64_t addr = dev->file_addr + block->read_len - 1;
+++      uint32_t len = MIN(block->read_len, block->block_len);
+++      uint64_t addr = dev->file_addr + len - 1;
++       dcr->EndBlock = (uint32_t)addr;
++       dcr->EndFile = (uint32_t)(addr >> 32);
++-      dev->block_num = dcr->EndBlock;
++-      dev->file = dcr->EndFile;
+++      dev->block_num = dev->EndBlock = dcr->EndBlock;
+++      dev->file = dev->EndFile = dcr->EndFile;
++    }
++    dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
++    dev->file_addr += block->read_len;
+Index: patches/testing/find_smallest_volfile.patch
+===================================================================
+--- patches/testing/find_smallest_volfile.patch        (revision 8203)
++++ patches/testing/find_smallest_volfile.patch        (working copy)
+@@ -149,74 +149,3 @@
+     return return_bsr;
+  }
+  
+-@@ -386,8 +397,6 @@
+-          rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
+-       goto no_match;
+-    }
+--   Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n", 
+--      rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
+- 
+-    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
+-       Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
+-@@ -411,6 +420,9 @@
+-    Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
+-          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
+- 
+-+   Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n", 
+-+      rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
+-+
+-    if (!match_fileregex(bsr, rec, jcr)) {
+-      Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
+-      goto no_match;
+-@@ -607,14 +619,7 @@
+- 
+- static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done)
+- {
+--   /*
+--    * Currently block matching does not work correctly for disk
+--    * files in all cases, so it is "turned off" by the following 
+--    * return statement.
+--    */
+--   return 1;
+- 
+--
+-    if (!volblock) {
+-       return 1;                       /* no specification matches all */
+-    }
+-@@ -622,8 +627,9 @@
+-    if (rec->state & REC_ISTAPE) {
+-       return 1;                       /* All File records OK for this match */
+-    }
+--//  Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
+--//             volblock->sblock, volblock->eblock, rec->Block);
+-+   Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
+-+         volblock->sblock, volblock->eblock, rec->Block);
+-+
+-    if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
+-       return 1;
+-    }
+-Index: src/stored/read_record.c
+-===================================================================
+---- src/stored/read_record.c  (rĂ©vision 8116)
+-+++ src/stored/read_record.c  (copie de travail)
+-@@ -261,8 +261,8 @@
+-                Dmsg2(100, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
+-                break;
+-             } else if (rec->match_stat == 0) {  /* no match */
+--               Dmsg4(100, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
+--                  rec->remainder, rec->FileIndex, dev->file, dev->block_num);
+-+               Dmsg7(100, "BSR no match: clear rem=%d FI=%d rec->Block=%d dev->LastBlock=%d dev->EndBlock=%d before set_eof pos %u:%u\n",
+-+                     rec->remainder, rec->FileIndex, rec->Block, dev->LastBlock, dev->EndBlock, dev->file, dev->block_num);
+-                rec->remainder = 0;
+-                rec->state &= ~REC_PARTIAL_RECORD;
+-                if (try_repositioning(jcr, rec, dcr)) {
+-@@ -346,6 +346,9 @@
+-        */
+-       if (dev->file > bsr->volfile->sfile ||             
+-          (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
+-+         Dmsg4(dbglvl, _("Reposition from (file:block) %u:%u to %u:%u\n"),
+-+            dev->file, dev->block_num, bsr->volfile->sfile,
+-+            bsr->volblock->sblock);
+-          return false;
+-       }
+-       if (verbose) {