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