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 /* Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
608  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
609  *      row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
610  */
sendit(void * arg,int num_fields,char ** row)611 static int sendit(void *arg, int num_fields, char **row)
612 {
613    JCR *jcr = (JCR *)arg;
614 
615    if (job_canceled(jcr)) {
616       return 1;
617    }
618 
619    if (row[2][0] == '0') {           /* discard when file_index == 0 */
620       return 0;
621    }
622 
623    /* sending with checksum */
624    if (num_fields == 7
625        && row[6][0] /* skip checksum = '0' */
626        && row[6][1])
627    {
628       jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
629                              row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
630    } else {
631       jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
632                              row[0], row[1], 0, row[4], 0, 0, row[5]);
633    }
634    return 0;
635 }
636 
637 /* 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)638 static void scan_findex(JCR *jcr, RBSR *bsr,
639                         int32_t FirstIndex, int32_t LastIndex,
640                         int32_t &lastFileIndex, uint32_t &lastJobId)
641 {
642    RBSR_FINDEX *fi;
643    FILE_DBR fdbr;
644    memset(&fdbr, 0, sizeof(fdbr));
645 
646    fi = (RBSR_FINDEX *) bsr->fi_list->first();
647    while (fi) {
648       int32_t findex, findex2;
649 
650       /* fi points to the first item of the list, or the next item that is not
651        * contigous to the previous group
652        */
653       findex = fi->findex;
654       findex2 = fi->findex2;
655 
656       /* Sometime (with the restore command for example), the fi_list can
657        * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop
658        * is here to merge blocks and reduce the bsr output. The next while(fi)
659        * iteration will use the next_fi that points to the last merged element.
660        */
661       RBSR_FINDEX *next_fi;
662       for (next_fi = (RBSR_FINDEX*) bsr->fi_list->next(fi);
663            next_fi && next_fi->findex == (findex2+1);
664            next_fi = (RBSR_FINDEX *) bsr->fi_list->next(next_fi))
665       {
666          findex2 = next_fi->findex2;
667       }
668 
669       /* next_fi points after the current block (or to the end of the list), so
670        * the next while() iteration will use the next value
671        */
672       fi = next_fi;
673 
674       /* We look if the current FI block match the volume information */
675       if ((findex >= FirstIndex && findex <= LastIndex) ||
676           (findex2 >= FirstIndex && findex2 <= LastIndex) ||
677           (findex < FirstIndex && findex2 > LastIndex)) {
678 
679          findex = findex < FirstIndex ? FirstIndex : findex;
680          findex2 = findex2 > LastIndex ? LastIndex : findex2;
681 
682          bool dolist=false;
683          /* Display only new files */
684          if (findex != lastFileIndex || bsr->JobId != lastJobId) {
685             /* Not the same file, or not the same job */
686             fdbr.FileIndex = findex;
687             dolist = true;
688 
689          } else if (findex2 != lastFileIndex) {
690             /* We are in the same job, and the first index was already generated */
691             fdbr.FileIndex = findex + 1;
692             dolist = true;
693          }
694 
695          /* Keep the current values for the next loop */
696          lastJobId = bsr->JobId;
697          lastFileIndex = findex2;
698 
699          /* Generate if needed the list of files */
700          if (dolist) {
701             fdbr.FileIndex2 = findex2;
702             fdbr.JobId = bsr->JobId;
703             db_list_files(jcr, jcr->db, &fdbr, sendit, jcr);
704          }
705       }
706    }
707 }
708 
709 /*
710  * Scan bsr data for a single bsr record
711  */
scan_bsr_item(JCR * jcr,RBSR * bsr)712 static void scan_bsr_item(JCR *jcr, RBSR *bsr)
713 {
714    int32_t lastFileIndex=0;
715    uint32_t lastJobId=0;
716    /*
717     * For a given volume, loop over all the JobMedia records.
718     *   VolCount is the number of JobMedia records.
719     */
720    for (int i=0; i < bsr->VolCount; i++) {
721       if (!is_volume_selected(bsr->fi_list,
722                               bsr->VolParams[i].FirstIndex,
723                               bsr->VolParams[i].LastIndex))
724       {
725          continue;
726       }
727 
728       scan_findex(jcr, bsr,
729                   bsr->VolParams[i].FirstIndex,
730                   bsr->VolParams[i].LastIndex,
731                   lastFileIndex, lastJobId);
732    }
733 }
734 
735 /*
736  * We need to find all files from the BSR. All files are listed, this is used
737  * to send the list of the files to be restored to a plugin for example.
738  */
scan_bsr(JCR * jcr)739 void scan_bsr(JCR *jcr)
740 {
741    char *p;
742    JobId_t JobId;
743    RBSR *bsr;
744    if (!jcr->JobIds || *jcr->JobIds == 0) {
745       foreach_rblist(bsr, jcr->bsr_list) {
746          scan_bsr_item(jcr, bsr);
747       }
748       return;
749    }
750    for (p=jcr->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
751       foreach_rblist(bsr, jcr->bsr_list) {
752          if (JobId == bsr->JobId) {
753             scan_bsr_item(jcr, bsr);
754          }
755       }
756    }
757    return;
758 }
759