]> git.sur5r.net Git - bacula/bacula/blob - bacula/patches/testing/copy_list_copies_cmd.patch
ebl Too many external segfault in this way
[bacula/bacula] / bacula / patches / testing / copy_list_copies_cmd.patch
1 Index: src/dird/ua_output.c
2 ===================================================================
3 --- src/dird/ua_output.c        (revision 8203)
4 +++ src/dird/ua_output.c        (working copy)
5 @@ -223,6 +223,7 @@
6   *  list clients        - list clients
7   *  list nextvol job=xx  - list the next vol to be used by job
8   *  list nextvolume job=xx - same as above.
9 + *  list copies jobid=x,y,z
10   *
11   */
12  
13 @@ -454,6 +455,19 @@
14              }
15           }
16           list_nextvol(ua, n);
17 +      } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
18 +         char *jobids=NULL;
19 +         uint32_t limit=0;
20 +         for (j=i+1; j<ua->argc; j++) {
21 +            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
22 +               if (is_a_number_list(ua->argv[j])) {
23 +                  jobids = ua->argv[j];
24 +               }
25 +            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
26 +               limit = atoi(ua->argv[j]);
27 +            } 
28 +         }
29 +         db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
30        } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
31                   || strcasecmp(ua->argk[i], NT_("days")) == 0) {
32           /* Ignore it */
33 Index: src/dird/migrate.c
34 ===================================================================
35 --- src/dird/migrate.c  (revision 8203)
36 +++ src/dird/migrate.c  (working copy)
37 @@ -1158,13 +1158,17 @@
38        /*
39         * If we terminated a copy normally:
40         *   - copy any Log records to the new JobId
41 +       *   - set type="Job Copy" for the new job
42         */
43        if (jcr->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
44           /* Copy JobLog to new JobId */
45           Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) " 
46                        "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
47 -              new_jobid, old_jobid);
48 +              edit_uint64(mig_jcr->jr.JobId, ec7), old_jobid);
49           db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
50 +         Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
51 +              (char)JT_JOB_COPY, ec7);
52 +         db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
53        } 
54  
55        if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
56 Index: src/dird/ua_purge.c
57 ===================================================================
58 --- src/dird/ua_purge.c (revision 8203)
59 +++ src/dird/ua_purge.c (working copy)
60 @@ -360,6 +360,57 @@
61  }
62  
63  /*
64 + * Change the type of the next copy job to backup.
65 + * We need to upgrade the next copy of a normal job,
66 + * and also upgrade the next copy when the normal job
67 + * already have been purged.
68 + *
69 + *   JobId: 1   PriorJobId: 0    (original)
70 + *   JobId: 2   PriorJobId: 1    (first copy)
71 + *   JobId: 3   PriorJobId: 1    (second copy)
72 + *
73 + *   JobId: 2   PriorJobId: 1    (first copy, now regular backup)
74 + *   JobId: 3   PriorJobId: 1    (second copy)
75 + *
76 + *  => Search through PriorJobId in jobid and
77 + *                    PriorJobId in PriorJobId (jobid)
78 + */
79 +void upgrade_copies(UAContext *ua, char *jobs)
80 +{
81 +   POOL_MEM query(PM_MESSAGE);
82 +   
83 +   db_lock(ua->db);
84 +   /* Do it in two times for mysql */
85 +   Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS "
86 +                  "SELECT MIN(JobId) AS JobId FROM Job "     /* Choose the oldest job */
87 +                   "WHERE Type='%c' "
88 +                     "AND ( PriorJobId IN (%s) "
89 +                         "OR "
90 +                          " PriorJobId IN ( "
91 +                             "SELECT PriorJobId "
92 +                               "FROM Job "
93 +                              "WHERE JobId IN (%s) "
94 +                               " AND Type='B' "
95 +                            ") "
96 +                         ") "
97 +                   "GROUP BY PriorJobId ",           /* one result per copy */
98 +        JT_JOB_COPY, jobs, jobs);
99 +   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
100 +
101 +   /* Now upgrade first copy to Backup */
102 +   Mmsg(query, "UPDATE Job SET Type='B' "           /* JT_JOB_COPY => JT_BACKUP  */
103 +                "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )");
104 +
105 +   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
106 +
107 +   Mmsg(query, "DROP TABLE cpy_tmp");
108 +   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
109 +
110 +   db_unlock(ua->db);
111 +   Dmsg1(00, "Upgrade copies Log sql=%s\n", query.c_str());
112 +}
113 +
114 +/*
115   * Remove all records from catalog for a list of JobIds
116   */
117  void purge_jobs_from_catalog(UAContext *ua, char *jobs)
118 @@ -377,13 +428,15 @@
119     db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
120     Dmsg1(050, "Delete Log sql=%s\n", query.c_str());
121  
122 +   upgrade_copies(ua, jobs);
123 +
124     /* Now remove the Job record itself */
125     Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs);
126     db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
127 +
128     Dmsg1(050, "Delete Job sql=%s\n", query.c_str());
129  }
130  
131 -
132  void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr )
133  {} /* ***FIXME*** implement */
134  
135 Index: src/dird/ua_restore.c
136 ===================================================================
137 --- src/dird/ua_restore.c       (revision 8203)
138 +++ src/dird/ua_restore.c       (working copy)
139 @@ -444,6 +444,7 @@
140        "add_suffix",   /* 17 */
141        "regexwhere",   /* 18 */
142        "restoreclient", /* 19 */
143 +      "copies",        /* 20 */
144        NULL
145     };
146  
147 @@ -1138,9 +1139,10 @@
148     bool ok = false;
149     FILESET_DBR fsr;
150     CLIENT_DBR cr;
151 +   POOL_MEM other_filter(PM_MESSAGE);
152 +   POOL_MEM temp_filter(PM_MESSAGE);
153     char fileset_name[MAX_NAME_LENGTH];
154     char ed1[50], ed2[50];
155 -   char pool_select[MAX_NAME_LENGTH];
156     int i;
157  
158     /* Create temp tables */
159 @@ -1196,23 +1198,32 @@
160     }
161  
162     /* If Pool specified, add PoolId specification */
163 -   pool_select[0] = 0;
164     if (rx->pool) {
165        POOL_DBR pr;
166        memset(&pr, 0, sizeof(pr));
167        bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
168        if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
169 -         bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
170 -            edit_int64(pr.PoolId, ed1));
171 +         Mmsg(other_filter, " AND Media.PoolId=%s ", 
172 +              edit_int64(pr.PoolId, ed1));
173        } else {
174           ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
175        }
176     }
177 +   /* include copies or not in job selection */
178 +   if (find_arg(ua, NT_("copies")) > 0) {
179 +      Mmsg(temp_filter, "%s AND Job.Type IN ('%c', '%c') ", 
180 +           other_filter.c_str(), (char)JT_BACKUP, (char)JT_JOB_COPY);
181 +   } else {
182 +      Mmsg(temp_filter, "%s AND Job.Type = '%c' ", other_filter.c_str(),
183 +           (char)JT_BACKUP);
184 +   }
185 +   pm_strcpy(other_filter, temp_filter.c_str());
186  
187     /* Find JobId of last Full backup for this client, fileset */
188     edit_int64(cr.ClientId, ed1);
189     Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
190 -         pool_select);
191 +        other_filter.c_str());
192 +   Dmsg1(0, "sql=%s\n", rx->query);
193     if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
194        ua->error_msg("%s\n", db_strerror(ua->db));
195        goto bail_out;
196 @@ -1238,12 +1249,13 @@
197  
198     /* Now find most recent Differental Job after Full save, if any */
199     Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
200 -        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
201 +        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
202     if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
203        ua->warning_msg("%s\n", db_strerror(ua->db));
204     }
205     /* Now update JobTDate to lock onto Differental, if any */
206     rx->JobTDate = 0;
207 +   Dmsg1(0, "sql=%s\n", rx->query);
208     if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
209        ua->warning_msg("%s\n", db_strerror(ua->db));
210     }
211 @@ -1254,7 +1266,8 @@
212  
213     /* Now find all Incremental Jobs after Full/dif save */
214     Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
215 -        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
216 +        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
217 +   Dmsg1(0, "sql=%s\n", rx->query);
218     if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
219        ua->warning_msg("%s\n", db_strerror(ua->db));
220     }
221 @@ -1267,6 +1280,8 @@
222     }
223  
224     if (rx->JobIds[0] != 0) {
225 +      /* Display a list of all copies */
226 +      db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, prtit, ua, HORZ_LIST);
227        /* Display a list of Jobs selected for this restore */
228        db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
229        ok = true;
230 Index: src/dird/ua_cmds.c
231 ===================================================================
232 --- src/dird/ua_cmds.c  (revision 8203)
233 +++ src/dird/ua_cmds.c  (working copy)
234 @@ -123,7 +123,7 @@
235   { NT_("exit"),       quit_cmd,      _("exit = quit"),                                false},
236   { NT_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode"),   false},
237   { NT_("help"),       help_cmd,      _("print this command"),                         false},
238 - { NT_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog"), true},
239 + { NT_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn> | copies <jobid=nn>]; from catalog"), true},
240   { NT_("label"),      label_cmd,     _("label a tape"),                               false},
241   { NT_("llist"),      llist_cmd,     _("full or long list like list command"),        true},
242   { NT_("messages"),   messagescmd,   _("messages"),                                   false},
243 Index: src/cats/protos.h
244 ===================================================================
245 --- src/cats/protos.h   (revision 8203)
246 +++ src/cats/protos.h   (working copy)
247 @@ -124,6 +124,7 @@
248  void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
249  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);
250  void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
251 +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);
252  
253  /* sql_update.c */
254  bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
255 Index: src/cats/sql_cmds.c
256 ===================================================================
257 --- src/cats/sql_cmds.c (revision 8203)
258 +++ src/cats/sql_cmds.c (working copy)
259 @@ -286,7 +286,7 @@
260     "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s "
261     "AND Job.ClientId=%s "
262     "AND Job.StartTime<'%s' "
263 -   "AND Level='F' AND JobStatus='T' AND Type='B' "
264 +   "AND Level='F' AND JobStatus='T' "
265     "AND JobMedia.JobId=Job.JobId "
266     "AND Media.Enabled=1 "
267     "AND JobMedia.MediaId=Media.MediaId "
268 @@ -297,13 +297,14 @@
269  
270  const char *uar_full =
271     "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,"
272 -   "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
273 -   "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
274 -   "FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId "
275 -   "AND Level='F' AND JobStatus='T' AND Type='B' "
276 -   "AND Media.Enabled=1 "
277 -   "AND JobMedia.JobId=Job.JobId "
278 -   "AND JobMedia.MediaId=Media.MediaId";
279 +     "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
280 +     "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
281 +    "FROM temp1,Job,JobMedia,Media "
282 +   "WHERE temp1.JobId=Job.JobId "
283 +     "AND Level='F' AND JobStatus='T' "
284 +     "AND Media.Enabled=1 "
285 +     "AND JobMedia.JobId=Job.JobId "
286 +     "AND JobMedia.MediaId=Media.MediaId";
287  
288  const char *uar_dif =
289     "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
290 @@ -316,7 +317,7 @@
291     "AND JobMedia.JobId=Job.JobId "
292     "AND Media.Enabled=1 "
293     "AND JobMedia.MediaId=Media.MediaId "
294 -   "AND Job.Level='D' AND JobStatus='T' AND Type='B' "
295 +   "AND Job.Level='D' AND JobStatus='T' "
296     "AND Job.FileSetId=FileSet.FileSetId "
297     "AND FileSet.FileSet='%s' "
298     "%s"
299 @@ -333,7 +334,7 @@
300     "AND Media.Enabled=1 "
301     "AND JobMedia.JobId=Job.JobId "
302     "AND JobMedia.MediaId=Media.MediaId "
303 -   "AND Job.Level='I' AND JobStatus='T' AND Type='B' "
304 +   "AND Job.Level='I' AND JobStatus='T' "
305     "AND Job.FileSetId=FileSet.FileSetId "
306     "AND FileSet.FileSet='%s' "
307     "%s";
308 Index: src/cats/sql_list.c
309 ===================================================================
310 --- src/cats/sql_list.c (revision 8203)
311 +++ src/cats/sql_list.c (working copy)
312 @@ -242,6 +242,43 @@
313  }
314  
315  
316 +void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
317 +                            DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
318 +{
319 +   POOL_MEM str_limit(PM_MESSAGE);
320 +   POOL_MEM str_jobids(PM_MESSAGE);
321 +
322 +   if (limit > 0) {
323 +      Mmsg(str_limit, " LIMIT %d", limit);
324 +   }
325 +
326 +   if (JobIds && JobIds[0]) {
327 +      Mmsg(str_jobids, " AND (C.PriorJobId IN (%s) OR C.JobId IN (%s)) ", 
328 +           JobIds, JobIds);      
329 +   }
330 +
331 +   db_lock(mdb);
332 +   Mmsg(mdb->cmd, 
333 +   "SELECT DISTINCT C.PriorJobId AS JobId, C.Job, "
334 +                   "C.JobId AS CopyJobId, M.MediaType "
335 +     "FROM Job AS C " 
336 +     "JOIN JobMedia    USING (JobId) "
337 +     "JOIN Media AS M  USING (MediaId) "
338 +    "WHERE C.Type = '%c' %s ORDER BY C.PriorJobId DESC %s",
339 +        (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
340 +
341 +   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
342 +      goto bail_out;
343 +   }
344 +
345 +   list_result(jcr, mdb, sendit, ctx, type);
346 +
347 +   sql_free_result(mdb);
348 +
349 +bail_out:
350 +   db_unlock(mdb);
351 +}
352 +
353  void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
354                                DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
355  {
356 Index: src/jcr.h
357 ===================================================================
358 --- src/jcr.h   (revision 8203)
359 +++ src/jcr.h   (working copy)
360 @@ -60,11 +60,12 @@
361  #define JT_MIGRATED_JOB          'M'  /* A previous backup job that was migrated */
362  #define JT_VERIFY                'V'  /* Verify Job */
363  #define JT_RESTORE               'R'  /* Restore Job */
364 -#define JT_CONSOLE               'c'  /* console program */
365 +#define JT_CONSOLE               'U'  /* console program */
366  #define JT_SYSTEM                'I'  /* internal system "job" */
367  #define JT_ADMIN                 'D'  /* admin job */
368  #define JT_ARCHIVE               'A'  /* Archive Job */
369 -#define JT_COPY                  'C'  /* Copy Job */
370 +#define JT_JOB_COPY              'C'  /* Copy of a Job */
371 +#define JT_COPY                  'c'  /* Copy Job */
372  #define JT_MIGRATE               'g'  /* Migration Job */
373  #define JT_SCAN                  'S'  /* Scan Job */
374  
375 Index: src/lib/util.c
376 ===================================================================
377 --- src/lib/util.c      (revision 8203)
378 +++ src/lib/util.c      (working copy)
379 @@ -361,6 +361,9 @@
380     case JT_COPY:
381        str = _("Copy");
382        break;
383 +   case JT_JOB_COPY:
384 +      str = _("Job Copy");
385 +      break;
386     case JT_CONSOLE:
387        str = _("Console");
388        break;
389 Index: src/lib/protos.h
390 ===================================================================
391 --- src/lib/protos.h    (revision 8203)
392 +++ src/lib/protos.h    (working copy)
393 @@ -186,6 +186,7 @@
394  bool             size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
395  char             *edit_utime             (utime_t val, char *buf, int buf_len);
396  bool             is_a_number             (const char *num);
397 +bool             is_a_number_list        (const char *n);
398  bool             is_an_integer           (const char *n);
399  bool             is_name_valid           (char *name, POOLMEM **msg);
400  
401 Index: src/lib/edit.c
402 ===================================================================
403 --- src/lib/edit.c      (revision 8203)
404 +++ src/lib/edit.c      (working copy)
405 @@ -407,6 +407,27 @@
406  }
407  
408  /*
409 + * Check if specified string is a list of number or not
410 + */
411 +bool is_a_number_list(const char *n)
412 +{
413 +   bool previous_digit = false; 
414 +   bool digit_seen = false;
415 +   while (*n) {
416 +      if (B_ISDIGIT(*n)) {
417 +         previous_digit=true;
418 +         digit_seen = true;
419 +      } else if (*n == ',' && previous_digit) {
420 +         previous_digit = false;
421 +      } else {
422 +         return false;
423 +      }
424 +      n++;
425 +   }
426 +   return digit_seen && *n==0; 
427 +}
428 +
429 +/*
430   * Check if the specified string is an integer
431   */
432  bool is_an_integer(const char *n)
433 Index: patches/testing/copy_list_copies_cmd.patch
434 ===================================================================
435 --- patches/testing/copy_list_copies_cmd.patch  (revision 8203)
436 +++ patches/testing/copy_list_copies_cmd.patch  (working copy)
437 @@ -1,26 +1,38 @@
438  Index: src/dird/ua_output.c
439  ===================================================================
440 ---- src/dird/ua_output.c       (revision 8163)
441 +--- src/dird/ua_output.c       (revision 8203)
442  +++ src/dird/ua_output.c       (working copy)
443 -@@ -454,6 +454,15 @@
444 +@@ -223,6 +223,7 @@
445 +  *  list clients        - list clients
446 +  *  list nextvol job=xx  - list the next vol to be used by job
447 +  *  list nextvolume job=xx - same as above.
448 ++ *  list copies jobid=x,y,z
449 +  *
450 +  */
451
452 +@@ -454,6 +455,19 @@
453               }
454            }
455            list_nextvol(ua, n);
456  +      } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
457 ++         char *jobids=NULL;
458 ++         uint32_t limit=0;
459  +         for (j=i+1; j<ua->argc; j++) {
460  +            if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
461 -+               jr.JobId = str_to_int64(ua->argv[j]);
462 ++               if (is_a_number_list(ua->argv[j])) {
463 ++                  jobids = ua->argv[j];
464 ++               }
465  +            } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
466 -+               jr.limit = atoi(ua->argv[j]);
467 ++               limit = atoi(ua->argv[j]);
468  +            } 
469  +         }
470 -+         db_list_copies_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
471 ++         db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
472         } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
473                    || strcasecmp(ua->argk[i], NT_("days")) == 0) {
474            /* Ignore it */
475  Index: src/dird/migrate.c
476  ===================================================================
477 ---- src/dird/migrate.c (revision 8179)
478 +--- src/dird/migrate.c (revision 8203)
479  +++ src/dird/migrate.c (working copy)
480  @@ -1158,13 +1158,17 @@
481         /*
482 @@ -41,9 +53,183 @@
483         } 
484   
485         if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
486 +Index: src/dird/ua_purge.c
487 +===================================================================
488 +--- src/dird/ua_purge.c        (revision 8203)
489 ++++ src/dird/ua_purge.c        (working copy)
490 +@@ -360,6 +360,57 @@
491 + }
492
493 + /*
494 ++ * Change the type of the next copy job to backup.
495 ++ * We need to upgrade the next copy of a normal job,
496 ++ * and also upgrade the next copy when the normal job
497 ++ * already have been purged.
498 ++ *
499 ++ *   JobId: 1   PriorJobId: 0    (original)
500 ++ *   JobId: 2   PriorJobId: 1    (first copy)
501 ++ *   JobId: 3   PriorJobId: 1    (second copy)
502 ++ *
503 ++ *   JobId: 2   PriorJobId: 1    (first copy, now regular backup)
504 ++ *   JobId: 3   PriorJobId: 1    (second copy)
505 ++ *
506 ++ *  => Search through PriorJobId in jobid and
507 ++ *                    PriorJobId in PriorJobId (jobid)
508 ++ */
509 ++void upgrade_copies(UAContext *ua, char *jobs)
510 ++{
511 ++   POOL_MEM query(PM_MESSAGE);
512 ++   
513 ++   db_lock(ua->db);
514 ++   /* Do it in two times for mysql */
515 ++   Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS "
516 ++                  "SELECT MIN(JobId) AS JobId FROM Job "     /* Choose the oldest job */
517 ++                   "WHERE Type='%c' "
518 ++                     "AND ( PriorJobId IN (%s) "
519 ++                         "OR "
520 ++                          " PriorJobId IN ( "
521 ++                             "SELECT PriorJobId "
522 ++                               "FROM Job "
523 ++                              "WHERE JobId IN (%s) "
524 ++                               " AND Type='B' "
525 ++                            ") "
526 ++                         ") "
527 ++                   "GROUP BY PriorJobId ",           /* one result per copy */
528 ++        JT_JOB_COPY, jobs, jobs);
529 ++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
530 ++
531 ++   /* Now upgrade first copy to Backup */
532 ++   Mmsg(query, "UPDATE Job SET Type='B' "           /* JT_JOB_COPY => JT_BACKUP  */
533 ++                "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )");
534 ++
535 ++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
536 ++
537 ++   Mmsg(query, "DROP TABLE cpy_tmp");
538 ++   db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
539 ++
540 ++   db_unlock(ua->db);
541 ++   Dmsg1(00, "Upgrade copies Log sql=%s\n", query.c_str());
542 ++}
543 ++
544 ++/*
545 +  * Remove all records from catalog for a list of JobIds
546 +  */
547 + void purge_jobs_from_catalog(UAContext *ua, char *jobs)
548 +@@ -377,13 +428,15 @@
549 +    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
550 +    Dmsg1(050, "Delete Log sql=%s\n", query.c_str());
551
552 ++   upgrade_copies(ua, jobs);
553 ++
554 +    /* Now remove the Job record itself */
555 +    Mmsg(query, "DELETE FROM Job WHERE JobId IN (%s)", jobs);
556 +    db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL);
557 ++
558 +    Dmsg1(050, "Delete Job sql=%s\n", query.c_str());
559 + }
560
561 +-
562 + void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr )
563 + {} /* ***FIXME*** implement */
564
565 +Index: src/dird/ua_restore.c
566 +===================================================================
567 +--- src/dird/ua_restore.c      (revision 8203)
568 ++++ src/dird/ua_restore.c      (working copy)
569 +@@ -444,6 +444,7 @@
570 +       "add_suffix",   /* 17 */
571 +       "regexwhere",   /* 18 */
572 +       "restoreclient", /* 19 */
573 ++      "copies",        /* 20 */
574 +       NULL
575 +    };
576
577 +@@ -1138,9 +1139,10 @@
578 +    bool ok = false;
579 +    FILESET_DBR fsr;
580 +    CLIENT_DBR cr;
581 ++   POOL_MEM other_filter(PM_MESSAGE);
582 ++   POOL_MEM temp_filter(PM_MESSAGE);
583 +    char fileset_name[MAX_NAME_LENGTH];
584 +    char ed1[50], ed2[50];
585 +-   char pool_select[MAX_NAME_LENGTH];
586 +    int i;
587
588 +    /* Create temp tables */
589 +@@ -1196,23 +1198,32 @@
590 +    }
591
592 +    /* If Pool specified, add PoolId specification */
593 +-   pool_select[0] = 0;
594 +    if (rx->pool) {
595 +       POOL_DBR pr;
596 +       memset(&pr, 0, sizeof(pr));
597 +       bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
598 +       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
599 +-         bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
600 +-            edit_int64(pr.PoolId, ed1));
601 ++         Mmsg(other_filter, " AND Media.PoolId=%s ", 
602 ++              edit_int64(pr.PoolId, ed1));
603 +       } else {
604 +          ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
605 +       }
606 +    }
607 ++   /* include copies or not in job selection */
608 ++   if (find_arg(ua, NT_("copies")) > 0) {
609 ++      Mmsg(temp_filter, "%s AND Job.Type IN ('%c', '%c') ", 
610 ++           other_filter.c_str(), (char)JT_BACKUP, (char)JT_JOB_COPY);
611 ++   } else {
612 ++      Mmsg(temp_filter, "%s AND Job.Type = '%c' ", other_filter.c_str(),
613 ++           (char)JT_BACKUP);
614 ++   }
615 ++   pm_strcpy(other_filter, temp_filter.c_str());
616
617 +    /* Find JobId of last Full backup for this client, fileset */
618 +    edit_int64(cr.ClientId, ed1);
619 +    Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
620 +-         pool_select);
621 ++        other_filter.c_str());
622 ++   Dmsg1(0, "sql=%s\n", rx->query);
623 +    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
624 +       ua->error_msg("%s\n", db_strerror(ua->db));
625 +       goto bail_out;
626 +@@ -1238,12 +1249,13 @@
627
628 +    /* Now find most recent Differental Job after Full save, if any */
629 +    Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
630 +-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
631 ++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
632 +    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
633 +       ua->warning_msg("%s\n", db_strerror(ua->db));
634 +    }
635 +    /* Now update JobTDate to lock onto Differental, if any */
636 +    rx->JobTDate = 0;
637 ++   Dmsg1(0, "sql=%s\n", rx->query);
638 +    if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
639 +       ua->warning_msg("%s\n", db_strerror(ua->db));
640 +    }
641 +@@ -1254,7 +1266,8 @@
642
643 +    /* Now find all Incremental Jobs after Full/dif save */
644 +    Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
645 +-        edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
646 ++        edit_int64(cr.ClientId, ed2), fsr.FileSet, other_filter.c_str());
647 ++   Dmsg1(0, "sql=%s\n", rx->query);
648 +    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
649 +       ua->warning_msg("%s\n", db_strerror(ua->db));
650 +    }
651 +@@ -1267,6 +1280,8 @@
652 +    }
653
654 +    if (rx->JobIds[0] != 0) {
655 ++      /* Display a list of all copies */
656 ++      db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, prtit, ua, HORZ_LIST);
657 +       /* Display a list of Jobs selected for this restore */
658 +       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
659 +       ok = true;
660  Index: src/dird/ua_cmds.c
661  ===================================================================
662 ---- src/dird/ua_cmds.c (revision 8163)
663 +--- src/dird/ua_cmds.c (revision 8203)
664  +++ src/dird/ua_cmds.c (working copy)
665  @@ -123,7 +123,7 @@
666    { NT_("exit"),       quit_cmd,      _("exit = quit"),                                false},
667 @@ -56,48 +242,101 @@
668    { NT_("messages"),   messagescmd,   _("messages"),                                   false},
669  Index: src/cats/protos.h
670  ===================================================================
671 ---- src/cats/protos.h  (revision 8163)
672 +--- src/cats/protos.h  (revision 8203)
673  +++ src/cats/protos.h  (working copy)
674  @@ -124,6 +124,7 @@
675   void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
676   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);
677   void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
678 -+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
679 ++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);
680   
681   /* sql_update.c */
682   bool db_update_job_start_record(JCR *jcr, B_DB *db, JOB_DBR *jr);
683 +Index: src/cats/sql_cmds.c
684 +===================================================================
685 +--- src/cats/sql_cmds.c        (revision 8203)
686 ++++ src/cats/sql_cmds.c        (working copy)
687 +@@ -286,7 +286,7 @@
688 +    "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s "
689 +    "AND Job.ClientId=%s "
690 +    "AND Job.StartTime<'%s' "
691 +-   "AND Level='F' AND JobStatus='T' AND Type='B' "
692 ++   "AND Level='F' AND JobStatus='T' "
693 +    "AND JobMedia.JobId=Job.JobId "
694 +    "AND Media.Enabled=1 "
695 +    "AND JobMedia.MediaId=Media.MediaId "
696 +@@ -297,13 +297,14 @@
697
698 + const char *uar_full =
699 +    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,"
700 +-   "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
701 +-   "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
702 +-   "FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId "
703 +-   "AND Level='F' AND JobStatus='T' AND Type='B' "
704 +-   "AND Media.Enabled=1 "
705 +-   "AND JobMedia.JobId=Job.JobId "
706 +-   "AND JobMedia.MediaId=Media.MediaId";
707 ++     "Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
708 ++     "StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
709 ++    "FROM temp1,Job,JobMedia,Media "
710 ++   "WHERE temp1.JobId=Job.JobId "
711 ++     "AND Level='F' AND JobStatus='T' "
712 ++     "AND Media.Enabled=1 "
713 ++     "AND JobMedia.JobId=Job.JobId "
714 ++     "AND JobMedia.MediaId=Media.MediaId";
715
716 + const char *uar_dif =
717 +    "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
718 +@@ -316,7 +317,7 @@
719 +    "AND JobMedia.JobId=Job.JobId "
720 +    "AND Media.Enabled=1 "
721 +    "AND JobMedia.MediaId=Media.MediaId "
722 +-   "AND Job.Level='D' AND JobStatus='T' AND Type='B' "
723 ++   "AND Job.Level='D' AND JobStatus='T' "
724 +    "AND Job.FileSetId=FileSet.FileSetId "
725 +    "AND FileSet.FileSet='%s' "
726 +    "%s"
727 +@@ -333,7 +334,7 @@
728 +    "AND Media.Enabled=1 "
729 +    "AND JobMedia.JobId=Job.JobId "
730 +    "AND JobMedia.MediaId=Media.MediaId "
731 +-   "AND Job.Level='I' AND JobStatus='T' AND Type='B' "
732 ++   "AND Job.Level='I' AND JobStatus='T' "
733 +    "AND Job.FileSetId=FileSet.FileSetId "
734 +    "AND FileSet.FileSet='%s' "
735 +    "%s";
736  Index: src/cats/sql_list.c
737  ===================================================================
738 ---- src/cats/sql_list.c        (revision 8163)
739 +--- src/cats/sql_list.c        (revision 8203)
740  +++ src/cats/sql_list.c        (working copy)
741  @@ -242,6 +242,43 @@
742   }
743   
744   
745 -+void db_list_copies_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr,
746 ++void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
747  +                            DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
748  +{
749 -+   char ed1[50];
750 -+   POOL_MEM limit(PM_MESSAGE);
751 -+   POOL_MEM jobids(PM_MESSAGE);
752 ++   POOL_MEM str_limit(PM_MESSAGE);
753 ++   POOL_MEM str_jobids(PM_MESSAGE);
754  +
755 -+   if (jr->limit > 0) {
756 -+      Mmsg(limit, " LIMIT %d", jr->limit);
757 ++   if (limit > 0) {
758 ++      Mmsg(str_limit, " LIMIT %d", limit);
759  +   }
760  +
761 -+   if (jr->JobId) {
762 -+      Mmsg(jobids, " AND (C.PriorJobId = %s OR C.JobId = %s) ", 
763 -+           edit_int64(jr->JobId, ed1),ed1);      
764 ++   if (JobIds && JobIds[0]) {
765 ++      Mmsg(str_jobids, " AND (C.PriorJobId IN (%s) OR C.JobId IN (%s)) ", 
766 ++           JobIds, JobIds);      
767  +   }
768  +
769  +   db_lock(mdb);
770  +   Mmsg(mdb->cmd, 
771 -+   "SELECT C.PriorJobId AS JobId, C.Job, C.JobId AS CopyJobId, M.MediaType "
772 ++   "SELECT DISTINCT C.PriorJobId AS JobId, C.Job, "
773 ++                   "C.JobId AS CopyJobId, M.MediaType "
774  +     "FROM Job AS C " 
775  +     "JOIN JobMedia    USING (JobId) "
776  +     "JOIN Media AS M  USING (MediaId) "
777  +    "WHERE C.Type = '%c' %s ORDER BY C.PriorJobId DESC %s",
778 -+        (char) JT_JOB_COPY, jobids.c_str(), limit.c_str());
779 ++        (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
780  +
781  +   if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
782  +      goto bail_out;
783 @@ -116,7 +355,7 @@
784   {
785  Index: src/jcr.h
786  ===================================================================
787 ---- src/jcr.h  (revision 8163)
788 +--- src/jcr.h  (revision 8203)
789  +++ src/jcr.h  (working copy)
790  @@ -60,11 +60,12 @@
791   #define JT_MIGRATED_JOB          'M'  /* A previous backup job that was migrated */
792 @@ -135,7 +374,7 @@
793   
794  Index: src/lib/util.c
795  ===================================================================
796 ---- src/lib/util.c     (revision 8163)
797 +--- src/lib/util.c     (revision 8203)
798  +++ src/lib/util.c     (working copy)
799  @@ -361,6 +361,9 @@
800      case JT_COPY:
801 @@ -147,3 +386,47 @@
802      case JT_CONSOLE:
803         str = _("Console");
804         break;
805 +Index: src/lib/protos.h
806 +===================================================================
807 +--- src/lib/protos.h   (revision 8203)
808 ++++ src/lib/protos.h   (working copy)
809 +@@ -186,6 +186,7 @@
810 + bool             size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
811 + char             *edit_utime             (utime_t val, char *buf, int buf_len);
812 + bool             is_a_number             (const char *num);
813 ++bool             is_a_number_list        (const char *n);
814 + bool             is_an_integer           (const char *n);
815 + bool             is_name_valid           (char *name, POOLMEM **msg);
816
817 +Index: src/lib/edit.c
818 +===================================================================
819 +--- src/lib/edit.c     (revision 8203)
820 ++++ src/lib/edit.c     (working copy)
821 +@@ -407,6 +407,27 @@
822 + }
823
824 + /*
825 ++ * Check if specified string is a list of number or not
826 ++ */
827 ++bool is_a_number_list(const char *n)
828 ++{
829 ++   bool previous_digit = false; 
830 ++   bool digit_seen = false;
831 ++   while (*n) {
832 ++      if (B_ISDIGIT(*n)) {
833 ++         previous_digit=true;
834 ++         digit_seen = true;
835 ++      } else if (*n == ',' && previous_digit) {
836 ++         previous_digit = false;
837 ++      } else {
838 ++         return false;
839 ++      }
840 ++      n++;
841 ++   }
842 ++   return digit_seen && *n==0; 
843 ++}
844 ++
845 ++/*
846 +  * Check if the specified string is an integer
847 +  */
848 + bool is_an_integer(const char *n)
849 Index: patches/testing/fix_1190.patch
850 ===================================================================
851 --- patches/testing/fix_1190.patch      (revision 8203)
852 +++ patches/testing/fix_1190.patch      (working copy)
853 @@ -201,7 +201,6 @@
854      }
855  -//  Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
856  -//             volblock->sblock, volblock->eblock, rec->Block);
857 --   if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
858  +   Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
859  +         volblock->sblock, volblock->eblock, rec->Block);
860  +
861 @@ -213,8 +212,8 @@
862  +    * But, we are already decoding rec->Block-1Block records
863  +    */
864  +   uint32_t max = volblock->eblock+DEFAULT_BLOCK_SIZE;
865 -+//   if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
866 -+   if (min <= rec->Block && max >= rec->Block) {
867 +    if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
868 ++//   if (min <= rec->Block && max >= rec->Block) {
869         return 1;
870      }
871      /* Once we get past last eblock, we are done */
872 @@ -288,3 +287,23 @@
873      }
874      /*
875       * Check for Start or End of Session Record
876 +Index: block.c
877 +===================================================================
878 +--- block.c    (rĂ©vision 8116)
879 ++++ block.c    (copie de travail)
880 +@@ -1116,11 +1116,12 @@
881 +       dcr->EndBlock = dev->EndBlock;
882 +       dcr->EndFile  = dev->EndFile;
883 +    } else {
884 +-      uint64_t addr = dev->file_addr + block->read_len - 1;
885 ++      uint32_t len = MIN(block->read_len, block->block_len);
886 ++      uint64_t addr = dev->file_addr + len - 1;
887 +       dcr->EndBlock = (uint32_t)addr;
888 +       dcr->EndFile = (uint32_t)(addr >> 32);
889 +-      dev->block_num = dcr->EndBlock;
890 +-      dev->file = dcr->EndFile;
891 ++      dev->block_num = dev->EndBlock = dcr->EndBlock;
892 ++      dev->file = dev->EndFile = dcr->EndFile;
893 +    }
894 +    dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
895 +    dev->file_addr += block->read_len;
896 Index: patches/testing/find_smallest_volfile.patch
897 ===================================================================
898 --- patches/testing/find_smallest_volfile.patch (revision 8203)
899 +++ patches/testing/find_smallest_volfile.patch (working copy)
900 @@ -149,74 +149,3 @@
901      return return_bsr;
902   }
903   
904 -@@ -386,8 +397,6 @@
905 -          rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
906 -       goto no_match;
907 -    }
908 --   Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n", 
909 --      rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
910
911 -    if (!match_sesstime(bsr, bsr->sesstime, rec, 1)) {
912 -       Dmsg2(dbglevel, "Fail on sesstime. bsr=%u rec=%u\n",
913 -@@ -411,6 +420,9 @@
914 -    Dmsg3(dbglevel, "match on findex=%d. bsr=%d,%d\n",
915 -          rec->FileIndex, bsr->FileIndex->findex, bsr->FileIndex->findex2);
916
917 -+   Dmsg3(dbglevel, "OK bsr Block=%u. bsr=%u,%u\n", 
918 -+      rec->Block, bsr->volblock->sblock, bsr->volblock->eblock);
919 -+
920 -    if (!match_fileregex(bsr, rec, jcr)) {
921 -      Dmsg1(dbglevel, "Fail on fileregex='%s'\n", bsr->fileregex);
922 -      goto no_match;
923 -@@ -607,14 +619,7 @@
924
925 - static int match_volblock(BSR *bsr, BSR_VOLBLOCK *volblock, DEV_RECORD *rec, bool done)
926 - {
927 --   /*
928 --    * Currently block matching does not work correctly for disk
929 --    * files in all cases, so it is "turned off" by the following 
930 --    * return statement.
931 --    */
932 --   return 1;
933
934 --
935 -    if (!volblock) {
936 -       return 1;                       /* no specification matches all */
937 -    }
938 -@@ -622,8 +627,9 @@
939 -    if (rec->state & REC_ISTAPE) {
940 -       return 1;                       /* All File records OK for this match */
941 -    }
942 --//  Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
943 --//             volblock->sblock, volblock->eblock, rec->Block);
944 -+   Dmsg3(dbglevel, "match_volblock: sblock=%u eblock=%u recblock=%u\n",
945 -+         volblock->sblock, volblock->eblock, rec->Block);
946 -+
947 -    if (volblock->sblock <= rec->Block && volblock->eblock >= rec->Block) {
948 -       return 1;
949 -    }
950 -Index: src/stored/read_record.c
951 -===================================================================
952 ---- src/stored/read_record.c   (rĂ©vision 8116)
953 -+++ src/stored/read_record.c   (copie de travail)
954 -@@ -261,8 +261,8 @@
955 -                Dmsg2(100, "All done=(file:block) %u:%u\n", dev->file, dev->block_num);
956 -                break;
957 -             } else if (rec->match_stat == 0) {  /* no match */
958 --               Dmsg4(100, "BSR no match: clear rem=%d FI=%d before set_eof pos %u:%u\n",
959 --                  rec->remainder, rec->FileIndex, dev->file, dev->block_num);
960 -+               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",
961 -+                     rec->remainder, rec->FileIndex, rec->Block, dev->LastBlock, dev->EndBlock, dev->file, dev->block_num);
962 -                rec->remainder = 0;
963 -                rec->state &= ~REC_PARTIAL_RECORD;
964 -                if (try_repositioning(jcr, rec, dcr)) {
965 -@@ -346,6 +346,9 @@
966 -        */
967 -       if (dev->file > bsr->volfile->sfile ||             
968 -          (dev->file == bsr->volfile->sfile && dev->block_num > bsr->volblock->sblock)) {
969 -+         Dmsg4(dbglvl, _("Reposition from (file:block) %u:%u to %u:%u\n"),
970 -+            dev->file, dev->block_num, bsr->volfile->sfile,
971 -+            bsr->volblock->sblock);
972 -          return false;
973 -       }
974 -       if (verbose) {