1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- Bootstrap Record routines.
22  *
23  *      BSR (bootstrap record) handling routines split from
24  *        ua_restore.c July MMIII
25  *
26  *     Kern Sibbald, July MMII
27  *
28  */
29 
30 #include "bacula.h"
31 #include "dird.h"
32 
33 /* Forward referenced functions */
34 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
35 
36 /*
37  * Create new FileIndex entry for BSR
38  */
new_findex()39 RBSR_FINDEX *new_findex()
40 {
41    RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
42    memset(fi, 0, sizeof(RBSR_FINDEX));
43    return fi;
44 }
45 
46 /*
47  * Get storage device name from Storage resource
48  */
get_storage_device(char * device,char * storage)49 static bool get_storage_device(char *device, char *storage)
50 {
51    STORE *store;
52    if (storage[0] == 0) {
53       return false;
54    }
55    store = (STORE *)GetResWithName(R_STORAGE, storage);
56    if (!store) {
57       return false;
58    }
59    DEVICE *dev = (DEVICE *)(store->device->first());
60    if (!dev) {
61       return false;
62    }
63    bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
64    return true;
65 }
66 
67 /*
68  * Our data structures were not designed completely
69  *  correctly, so the file indexes cover the full
70  *  range regardless of volume. The FirstIndex and LastIndex
71  *  passed in here are for the current volume, so when
72  *  writing out the fi, constrain them to those values.
73  *
74  * We are called here once for each JobMedia record
75  *  for each Volume.
76  */
write_findex(rblist * fi_list,int32_t FirstIndex,int32_t LastIndex,FILE * fd)77 static uint32_t write_findex(rblist *fi_list,
78               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
79 {
80    RBSR_FINDEX *fi;
81    uint32_t count = 0;
82 
83    fi = (RBSR_FINDEX *) fi_list->first();
84    while (fi) {
85       int32_t findex, findex2;
86 
87       /* fi points to the first item of the list, or the next item that is not
88        * contigous to the previous group
89        */
90       findex = fi->findex;
91       findex2 = fi->findex2;
92 
93       /* Sometime (with the restore command for example), the fi_list can
94        * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop
95        * is here to merge blocks and reduce the bsr output. The next while(fi)
96        * iteration will use the next_fi that points to the last merged element.
97        */
98       RBSR_FINDEX *next_fi;
99       for (next_fi = (RBSR_FINDEX*) fi_list->next(fi);
100            next_fi && next_fi->findex == (findex2+1);
101            next_fi = (RBSR_FINDEX *) fi_list->next(next_fi))
102       {
103          findex2 = next_fi->findex2;
104       }
105 
106       /* next_fi points after the current block (or to the end of the list), so
107        * the next while() iteration will use the next value
108        */
109       fi = next_fi;
110 
111       /* We look if the current FI block match the volume information */
112       if ((findex >= FirstIndex && findex <= LastIndex) ||
113           (findex2 >= FirstIndex && findex2 <= LastIndex) ||
114           (findex < FirstIndex && findex2 > LastIndex)) {
115 
116          findex = findex < FirstIndex ? FirstIndex : findex;
117          findex2 = findex2 > LastIndex ? LastIndex : findex2;
118 
119          if (findex == findex2) {
120             fprintf(fd, "FileIndex=%d\n", findex);
121             count++;
122          } else {
123             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
124             count += findex2 - findex + 1;
125          }
126       }
127    }
128 
129    return count;
130 }
131 
132 /*
133  * Find out if Volume defined with FirstIndex and LastIndex
134  *   falls within the range of selected files in the bsr.
135  */
is_volume_selected(rblist * fi_list,int32_t FirstIndex,int32_t LastIndex)136 static bool is_volume_selected(rblist *fi_list,
137               int32_t FirstIndex, int32_t LastIndex)
138 {
139    RBSR_FINDEX *fi;
140    foreach_rblist(fi, fi_list) {
141       if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
142           (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
143           (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
144          return true;
145       }
146    }
147    return false;
148 }
149 
150 
151 /* Create a new bootstrap record */
new_bsr()152 RBSR *new_bsr()
153 {
154    RBSR_FINDEX *fi=NULL;
155    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
156    memset(bsr, 0, sizeof(RBSR));
157    bsr->fi_list = New(rblist(fi, &fi->link));
158    return bsr;
159 }
160 
161 /* Free the entire BSR */
free_bsr(rblist * bsr_list)162 void free_bsr(rblist *bsr_list)
163 {
164    RBSR *bsr;
165    foreach_rblist(bsr, bsr_list) {
166       delete bsr->fi_list;
167       if (bsr->VolParams) {
168          free(bsr->VolParams);
169       }
170       if (bsr->fileregex) {
171          free(bsr->fileregex);
172       }
173       if (bsr->m_fi) {
174          free(bsr->m_fi);
175       }
176    }
177    delete bsr_list;
178 }
179 
180 /*
181  * Complete the BSR by filling in the VolumeName and
182  *  VolSessionId and VolSessionTime using the JobId
183  */
complete_bsr(UAContext * ua,rblist * bsr_list)184 bool complete_bsr(UAContext *ua, rblist *bsr_list)
185 {
186    RBSR *bsr;
187    foreach_rblist(bsr, bsr_list) {
188       JOB_DBR jr;
189       memset(&jr, 0, sizeof(jr));
190       jr.JobId = bsr->JobId;
191       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
192          ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
193          return false;
194       }
195       bsr->VolSessionId = jr.VolSessionId;
196       bsr->VolSessionTime = jr.VolSessionTime;
197       if (jr.JobFiles == 0) {      /* zero files is OK, not an error, but */
198          bsr->VolCount = 0;        /*   there are no volumes */
199          continue;
200       }
201       if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
202            &(bsr->VolParams))) == 0) {
203          ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
204          if (bsr->VolParams) {
205             free(bsr->VolParams);
206             bsr->VolParams = NULL;
207          }
208          return false;
209       }
210    }
211    return true;
212 }
213 
214 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
215 static uint32_t uniq = 0;
216 
make_unique_restore_filename(UAContext * ua,POOL_MEM & fname)217 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
218 {
219    JCR *jcr = ua->jcr;
220    int i = find_arg_with_value(ua, "bootstrap");
221    if (i >= 0) {
222       Mmsg(fname, "%s", ua->argv[i]);
223       jcr->unlink_bsr = false;
224    } else {
225       P(mutex);
226       uniq++;
227       V(mutex);
228       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
229       jcr->unlink_bsr = true;
230    }
231    if (jcr->RestoreBootstrap) {
232       free(jcr->RestoreBootstrap);
233    }
234    jcr->RestoreBootstrap = bstrdup(fname.c_str());
235 }
236 
237 /*
238  * Write the bootstrap records to file
239  */
write_bsr_file(UAContext * ua,RESTORE_CTX & rx)240 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
241 {
242    FILE *fd;
243    POOL_MEM fname(PM_MESSAGE);
244    uint32_t count = 0;;
245    bool err;
246 
247    make_unique_restore_filename(ua, fname);
248    fd = bfopen(fname.c_str(), "w+b");
249    if (!fd) {
250       berrno be;
251       ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
252          fname.c_str(), be.bstrerror());
253       goto bail_out;
254    }
255    /* Write them to file */
256    count = write_bsr(ua, rx, fd);
257    err = ferror(fd);
258    fclose(fd);
259    if (count == 0) {
260       ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
261       goto bail_out;
262    }
263    if (err) {
264       ua->error_msg(_("Error writing bsr file.\n"));
265       count = 0;
266       goto bail_out;
267    }
268 
269    if (chk_dbglvl(10)) {
270       print_bsr(ua, rx);
271    }
272 
273 bail_out:
274    return count;
275 }
276 
display_vol_info(UAContext * ua,RESTORE_CTX & rx,JobId_t JobId)277 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
278 {
279    POOL_MEM volmsg(PM_MESSAGE);
280    char Device[MAX_NAME_LENGTH];
281    char online;
282    RBSR *bsr;
283 
284    foreach_rblist(bsr, rx.bsr_list) {
285       if (JobId && JobId != bsr->JobId) {
286          continue;
287       }
288 
289       for (int i=0; i < bsr->VolCount; i++) {
290          if (bsr->VolParams[i].VolumeName[0]) {
291             if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
292                Device[0] = 0;
293             }
294             if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
295                online = '*';
296             } else {
297                online = ' ';
298             }
299             Mmsg(volmsg, "%c%-25s %-25s %-25s",
300                  online, bsr->VolParams[i].VolumeName,
301                  bsr->VolParams[i].Storage, Device);
302             add_prompt(ua, volmsg.c_str());
303          }
304       }
305    }
306 }
307 
display_bsr_info(UAContext * ua,RESTORE_CTX & rx)308 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
309 {
310    char *p;
311    JobId_t JobId;
312 
313    /* Tell the user what he will need to mount */
314    ua->send_msg("\n");
315    ua->send_msg(_("The Job will require the following (*=>InChanger):\n"
316                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
317                   "===========================================================================\n"));
318    /* Create Unique list of Volumes using prompt list */
319    start_prompt(ua, "");
320    if (*rx.JobIds == 0) {
321       /* Print Volumes in any order */
322       display_vol_info(ua, rx, 0);
323    } else {
324       /* Ensure that the volumes are printed in JobId order */
325       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
326          display_vol_info(ua, rx, JobId);
327       }
328    }
329    for (int i=0; i < ua->num_prompts; i++) {
330       ua->send_msg("   %s\n", ua->prompt[i]);
331       free(ua->prompt[i]);
332       if (ua->unique[i]) free(ua->unique[i]);
333    }
334    if (ua->num_prompts == 0) {
335       ua->send_msg(_("No Volumes found to restore.\n"));
336    } else {
337       ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
338    }
339    ua->num_prompts = 0;
340    ua->send_msg("\n");
341 
342    return;
343 }
344 
345 /*
346  * Write bsr data for a single bsr record
347  */
write_bsr_item(RBSR * bsr,UAContext * ua,RESTORE_CTX & rx,FILE * fd,bool & first,uint32_t & LastIndex)348 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua,
349                    RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
350 {
351    char ed1[50], ed2[50];
352    uint32_t count = 0;
353    uint32_t total_count = 0;
354    char device[MAX_NAME_LENGTH];
355 
356    /*
357     * For a given volume, loop over all the JobMedia records.
358     *   VolCount is the number of JobMedia records.
359     */
360    for (int i=0; i < bsr->VolCount; i++) {
361       if (!is_volume_selected(bsr->fi_list, bsr->VolParams[i].FirstIndex,
362            bsr->VolParams[i].LastIndex)) {
363          bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
364          continue;
365       }
366       if (!rx.store) {
367          find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
368                                        bsr->VolParams[i].MediaType);
369       }
370       fprintf(fd, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
371       fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
372       fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
373       if (bsr->fileregex) {
374          fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
375       }
376       if (get_storage_device(device, bsr->VolParams[i].Storage)) {
377          fprintf(fd, "Device=\"%s\"\n", device);
378       }
379       if (bsr->VolParams[i].Slot > 0) {
380          fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
381       }
382       fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
383       fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
384       fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
385               edit_uint64(bsr->VolParams[i].EndAddr, ed2));
386       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
387             bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
388 
389       count = write_findex(bsr->fi_list, bsr->VolParams[i].FirstIndex,
390                            bsr->VolParams[i].LastIndex, fd);
391       if (count) {
392          fprintf(fd, "Count=%u\n", count);
393       }
394       total_count += count;
395       /* If the same file is present on two tapes or in two files
396        *   on a tape, it is a continuation, and should not be treated
397        *   twice in the totals.
398        */
399       if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
400          total_count--;
401       }
402       first = false;
403       LastIndex = bsr->VolParams[i].LastIndex;
404    }
405    return total_count;
406 }
407 
408 
409 /*
410  * Here we actually write out the details of the bsr file.
411  *  Note, there is one bsr for each JobId, but the bsr may
412  *  have multiple volumes, which have been entered in the
413  *  order they were written.
414  * The bsrs must be written out in the order the JobIds
415  *  are found in the jobid list.
416  */
write_bsr(UAContext * ua,RESTORE_CTX & rx,FILE * fd)417 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
418 {
419    bool first = true;
420    uint32_t LastIndex = 0;
421    uint32_t total_count = 0;
422    char *p;
423    JobId_t JobId;
424    RBSR *bsr;
425    if (*rx.JobIds == 0) {
426       foreach_rblist(bsr, rx.bsr_list) {
427          total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
428       }
429       return total_count;
430    }
431    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
432       foreach_rblist(bsr, rx.bsr_list) {
433          if (JobId == bsr->JobId) {
434             total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
435          }
436       }
437    }
438    return total_count;
439 }
440 
print_bsr(UAContext * ua,RESTORE_CTX & rx)441 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
442 {
443    write_bsr(ua, rx, stdout);
444 }
445 
search_rbsr(void * elt1,void * elt2)446 static int search_rbsr(void *elt1, void *elt2)
447 {
448    RBSR *bsr1 = (RBSR *)elt1;
449    RBSR *bsr = (RBSR *)elt2;
450 
451    /* We might replace by a simple JobId - JobId */
452    if (bsr->JobId == bsr1->JobId) {
453       return 0;
454 
455    } else if (bsr->JobId < bsr1->JobId) {
456       return 1;
457    }
458 
459    return -1;
460 }
461 
search_fi(void * elt1,void * elt2)462 static int search_fi(void *elt1, void *elt2)
463 {
464    RBSR_FINDEX *f1 = (RBSR_FINDEX *) elt1;
465    RBSR_FINDEX *f2 = (RBSR_FINDEX *) elt2;
466 
467    if (f1->findex == (f2->findex - 1)) {
468       return 0;
469 
470    } else if (f1->findex2 == (f2->findex2 + 1)) {
471       return 0;
472 
473    } else if (f1->findex >= f2->findex && f1->findex2 <= f2->findex2) {
474       return 0;
475    }
476 
477    return (f1->findex > f2->findex) ? 1 : -1;
478 }
479 
create_bsr_list(uint32_t JobId,int findex,int findex2)480 rblist *create_bsr_list(uint32_t JobId, int findex, int findex2)
481 {
482    RBSR *bsr = NULL;
483    RBSR_FINDEX *fi = NULL;
484    rblist *bsr_list = New(rblist(bsr, &bsr->link));
485 
486    bsr = new_bsr();
487    bsr->JobId = JobId;
488 
489    bsr_list->insert(bsr, search_rbsr);
490 
491    fi = new_findex();
492    fi->findex = findex;
493    fi->findex2 = findex2;
494 
495    bsr->fi_list->insert(fi, search_fi);
496 
497    return bsr_list;
498 }
499 
500 /*
501  * Add a FileIndex to the list of BootStrap records.
502  *  Here we are only dealing with JobId's and the FileIndexes
503  *  associated with those JobIds.
504  * We expect that JobId, FileIndex are sorted ascending.
505  *
506  * When doing restore from tree, FileIndex are not sorted, so it can
507  * create gaps.
508  */
add_findex(rblist * bsr_list,uint32_t JobId,int32_t findex)509 void add_findex(rblist *bsr_list, uint32_t JobId, int32_t findex)
510 {
511    RBSR *bsr, bsr2;
512    RBSR_FINDEX *fi, *nfi;
513 
514    if (findex == 0) {
515       return;                         /* probably a dummy directory */
516    }
517    if (findex < 0) findex = -findex;
518 
519    bsr2.JobId = JobId;
520    /* Walk down list of bsrs until we find the JobId */
521    bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
522 
523    /* The list is empty, or the JobId is not already in,
524     * Must add new JobId
525     */
526    if (!bsr) {
527       bsr = new_bsr();
528       bsr->JobId = JobId;
529       bsr_list->insert(bsr, search_rbsr);
530    }
531 
532    if (bsr->m_fi) {
533       fi = bsr->m_fi;
534 
535    } else {
536       fi = bsr->m_fi = new_findex();
537    }
538 
539    fi->findex  = findex;
540    fi->findex2 = findex;
541 
542    Dmsg1(1000, "Trying to insert %ld\n", findex);
543    /* try to insert our fi */
544    nfi = (RBSR_FINDEX*) bsr->fi_list->insert((void *)fi, search_fi);
545 
546    /* We found an existing one, extend it */
547    if (nfi != fi) {
548       if (findex == (nfi->findex2 + 1)) {
549          Dmsg2(1000, "Extend %ld-%ld\n", nfi->findex, findex);
550          nfi->findex2 = findex;
551 
552       } else if (findex == (nfi->findex - 1)) {
553          Dmsg2(1000, "Extend %ld-%ld\n", findex, nfi->findex2);
554          nfi->findex = findex;
555 
556       } else {
557          Dmsg2(1000, "Found the same values? %ld-%ld\n", nfi->findex, nfi->findex2);
558       }
559 
560    } else {
561       Dmsg2(1000, "Inserted %ld-%ld\n", fi->findex, fi->findex2);
562       bsr->m_fi = NULL;         /* comsumed */
563    }
564 }
565 
566 /*
567  * Add all possible  FileIndexes to the list of BootStrap records.
568  *  Here we are only dealing with JobId's and the FileIndexes
569  *  associated with those JobIds.
570  */
add_findex_all(rblist * bsr_list,uint32_t JobId,const char * fileregex)571 void add_findex_all(rblist *bsr_list, uint32_t JobId, const char *fileregex)
572 {
573    RBSR *bsr, bsr2;
574    RBSR_FINDEX *fi;
575 
576    bsr2.JobId = JobId;
577    /* Walk down list of bsrs until we find the JobId */
578    bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
579 
580    if (!bsr) {                    /* Must add new JobId */
581       fi = new_findex();
582       fi->findex = 1;
583       fi->findex2 = INT32_MAX;
584 
585       bsr = new_bsr();
586       bsr->JobId = JobId;
587       bsr->fi_list->insert(fi, search_fi);
588       bsr_list->insert(bsr, search_rbsr);
589 
590       if (fileregex) {
591          /* If we use regexp to restore, set it for each jobid */
592          bsr->fileregex = bstrdup(fileregex);
593       }
594       return;
595    }
596 
597    /*
598     * At this point, bsr points to bsr containing this JobId,
599     */
600    fi = new_findex();
601    fi->findex = 1;
602    fi->findex2 = INT32_MAX;
603    bsr->fi_list->insert(fi, search_fi);
604    return;
605 }
606 
607 #ifdef needed
608 /* Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
609  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
610  *      row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
611  */
sendit(void * arg,int num_fields,char ** row)612 static int sendit(void *arg, int num_fields, char **row)
613 {
614    JCR *jcr = (JCR *)arg;
615 
616    if (job_canceled(jcr)) {
617       return 1;
618    }
619 
620    if (row[2][0] == '0') {           /* discard when file_index == 0 */
621       return 0;
622    }
623 
624    /* sending with checksum */
625    if (num_fields == 7
626        && row[6][0] /* skip checksum = '0' */
627        && row[6][1])
628    {
629       jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
630                              row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
631    } else {
632       jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
633                              row[0], row[1], 0, row[4], 0, 0, row[5]);
634    }
635    return 0;
636 }
637 #endif
638 
639 /* We list all files for a given FI structure */
scan_findex(JCR * jcr,RBSR * bsr,int32_t FirstIndex,int32_t LastIndex,int32_t & lastFileIndex,uint32_t & lastJobId)640 static void scan_findex(JCR *jcr, RBSR *bsr,
641                         int32_t FirstIndex, int32_t LastIndex,
642                         int32_t &lastFileIndex, uint32_t &lastJobId)
643 {
644    RBSR_FINDEX *fi;
645    FILE_DBR fdbr;
646    memset(&fdbr, 0, sizeof(fdbr));
647 
648    fi = (RBSR_FINDEX *) bsr->fi_list->first();
649    while (fi) {
650       int32_t findex, findex2;
651 
652       /* fi points to the first item of the list, or the next item that is not
653        * contigous to the previous group
654        */
655       findex = fi->findex;
656       findex2 = fi->findex2;
657 
658       /* Sometime (with the restore command for example), the fi_list can
659        * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop
660        * is here to merge blocks and reduce the bsr output. The next while(fi)
661        * iteration will use the next_fi that points to the last merged element.
662        */
663       RBSR_FINDEX *next_fi;
664       for (next_fi = (RBSR_FINDEX*) bsr->fi_list->next(fi);
665            next_fi && next_fi->findex == (findex2+1);
666            next_fi = (RBSR_FINDEX *) bsr->fi_list->next(next_fi))
667       {
668          findex2 = next_fi->findex2;
669       }
670 
671       /* next_fi points after the current block (or to the end of the list), so
672        * the next while() iteration will use the next value
673        */
674       fi = next_fi;
675 
676       /* We look if the current FI block match the volume information */
677       if ((findex >= FirstIndex && findex <= LastIndex) ||
678           (findex2 >= FirstIndex && findex2 <= LastIndex) ||
679           (findex < FirstIndex && findex2 > LastIndex)) {
680 
681          findex = findex < FirstIndex ? FirstIndex : findex;
682          findex2 = findex2 > LastIndex ? LastIndex : findex2;
683 
684          bool dolist=false;
685          /* Display only new files */
686          if (findex != lastFileIndex || bsr->JobId != lastJobId) {
687             /* Not the same file, or not the same job */
688             fdbr.FileIndex = findex;
689             //dolist = true;
690 
691          } else if (findex2 != lastFileIndex) {
692             /* We are in the same job, and the first index was already generated */
693             fdbr.FileIndex = findex + 1;
694             //dolist = true;
695          }
696 
697          /* Keep the current values for the next loop */
698          lastJobId = bsr->JobId;
699          lastFileIndex = findex2;
700 
701          /* Generate if needed the list of files */
702          if (dolist) {
703             fdbr.FileIndex2 = findex2;
704             fdbr.JobId = bsr->JobId;
705             /* New code not working */
706             //db_list_files(jcr, jcr->db, &fdbr, sendit, jcr);
707          }
708       }
709    }
710 }
711 
712 /*
713  * Scan bsr data for a single bsr record
714  */
scan_bsr_item(JCR * jcr,RBSR * bsr)715 static void scan_bsr_item(JCR *jcr, RBSR *bsr)
716 {
717    int32_t lastFileIndex=0;
718    uint32_t lastJobId=0;
719    /*
720     * For a given volume, loop over all the JobMedia records.
721     *   VolCount is the number of JobMedia records.
722     */
723    for (int i=0; i < bsr->VolCount; i++) {
724       if (!is_volume_selected(bsr->fi_list,
725                               bsr->VolParams[i].FirstIndex,
726                               bsr->VolParams[i].LastIndex))
727       {
728          continue;
729       }
730 
731       scan_findex(jcr, bsr,
732                   bsr->VolParams[i].FirstIndex,
733                   bsr->VolParams[i].LastIndex,
734                   lastFileIndex, lastJobId);
735    }
736 }
737 
738 /*
739  * We need to find all files from the BSR. All files are listed, this is used
740  * to send the list of the files to be restored to a plugin for example.
741  */
scan_bsr(JCR * jcr)742 void scan_bsr(JCR *jcr)
743 {
744    char *p;
745    JobId_t JobId;
746    RBSR *bsr;
747    if (!jcr->JobIds || *jcr->JobIds == 0) {
748       foreach_rblist(bsr, jcr->bsr_list) {
749          scan_bsr_item(jcr, bsr);
750       }
751       return;
752    }
753    for (p=jcr->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
754       foreach_rblist(bsr, jcr->bsr_list) {
755          if (JobId == bsr->JobId) {
756             scan_bsr_item(jcr, bsr);
757          }
758       }
759    }
760    return;
761 }
762