2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Director -- Bootstrap Record routines.
23 * BSR (bootstrap record) handling routines split from
24 * ua_restore.c July MMIII
26 * Kern Sibbald, July MMII
33 /* Forward referenced functions */
34 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
37 * Create new FileIndex entry for BSR
39 RBSR_FINDEX *new_findex()
41 RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
42 memset(fi, 0, sizeof(RBSR_FINDEX));
47 * Get storage device name from Storage resource
49 static bool get_storage_device(char *device, char *storage)
52 if (storage[0] == 0) {
55 store = (STORE *)GetResWithName(R_STORAGE, storage);
59 DEVICE *dev = (DEVICE *)(store->device->first());
63 bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
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.
74 * We are called here once for each JobMedia record
77 static uint32_t write_findex(rblist *fi_list,
78 int32_t FirstIndex, int32_t LastIndex, FILE *fd)
83 fi = (RBSR_FINDEX *) fi_list->first();
85 int32_t findex, findex2;
87 /* fi points to the first item of the list, or the next item that is not
88 * contigous to the previous group
91 findex2 = fi->findex2;
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.
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))
103 findex2 = next_fi->findex2;
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
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)) {
116 findex = findex < FirstIndex ? FirstIndex : findex;
117 findex2 = findex2 > LastIndex ? LastIndex : findex2;
119 if (findex == findex2) {
120 fprintf(fd, "FileIndex=%d\n", findex);
123 fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
124 count += findex2 - findex + 1;
133 * Find out if Volume defined with FirstIndex and LastIndex
134 * falls within the range of selected files in the bsr.
136 static bool is_volume_selected(rblist *fi_list,
137 int32_t FirstIndex, int32_t LastIndex)
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)) {
151 /* Create a new bootstrap record */
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));
161 /* Free the entire BSR */
162 void free_bsr(rblist *bsr_list)
165 foreach_rblist(bsr, bsr_list) {
167 if (bsr->VolParams) {
168 free(bsr->VolParams);
170 if (bsr->fileregex) {
171 free(bsr->fileregex);
181 * Complete the BSR by filling in the VolumeName and
182 * VolSessionId and VolSessionTime using the JobId
184 bool complete_bsr(UAContext *ua, rblist *bsr_list)
187 foreach_rblist(bsr, bsr_list) {
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));
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 */
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;
214 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
215 static uint32_t uniq = 0;
217 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
220 int i = find_arg_with_value(ua, "bootstrap");
222 Mmsg(fname, "%s", ua->argv[i]);
223 jcr->unlink_bsr = false;
228 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
229 jcr->unlink_bsr = true;
231 if (jcr->RestoreBootstrap) {
232 free(jcr->RestoreBootstrap);
234 jcr->RestoreBootstrap = bstrdup(fname.c_str());
238 * Write the bootstrap records to file
240 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
243 POOL_MEM fname(PM_MESSAGE);
247 make_unique_restore_filename(ua, fname);
248 fd = bfopen(fname.c_str(), "w+b");
251 ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
252 fname.c_str(), be.bstrerror());
255 /* Write them to file */
256 count = write_bsr(ua, rx, fd);
260 ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
264 ua->error_msg(_("Error writing bsr file.\n"));
269 if (chk_dbglvl(10)) {
277 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
279 POOL_MEM volmsg(PM_MESSAGE);
280 char Device[MAX_NAME_LENGTH];
284 foreach_rblist(bsr, rx.bsr_list) {
285 if (JobId && JobId != bsr->JobId) {
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)) {
294 if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
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());
308 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
313 /* Tell the user what he will need to mount */
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);
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);
329 for (int i=0; i < ua->num_prompts; i++) {
330 ua->send_msg(" %s\n", ua->prompt[i]);
332 if (ua->unique[i]) free(ua->unique[i]);
334 if (ua->num_prompts == 0) {
335 ua->send_msg(_("No Volumes found to restore.\n"));
337 ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
346 * Write bsr data for a single bsr record
348 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua,
349 RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
351 char ed1[50], ed2[50];
353 uint32_t total_count = 0;
354 char device[MAX_NAME_LENGTH];
357 * For a given volume, loop over all the JobMedia records.
358 * VolCount is the number of JobMedia records.
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 */
367 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
368 bsr->VolParams[i].MediaType);
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);
376 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
377 fprintf(fd, "Device=\"%s\"\n", device);
379 if (bsr->VolParams[i].Slot > 0) {
380 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
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);
389 count = write_findex(bsr->fi_list, bsr->VolParams[i].FirstIndex,
390 bsr->VolParams[i].LastIndex, fd);
392 fprintf(fd, "Count=%u\n", count);
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.
399 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
403 LastIndex = bsr->VolParams[i].LastIndex;
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.
417 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
420 uint32_t LastIndex = 0;
421 uint32_t total_count = 0;
425 if (*rx.JobIds == 0) {
426 foreach_rblist(bsr, rx.bsr_list) {
427 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
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);
441 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
443 write_bsr(ua, rx, stdout);
446 static int search_rbsr(void *elt1, void *elt2)
448 RBSR *bsr1 = (RBSR *)elt1;
449 RBSR *bsr = (RBSR *)elt2;
451 /* We might replace by a simple JobId - JobId */
452 if (bsr->JobId == bsr1->JobId) {
455 } else if (bsr->JobId < bsr1->JobId) {
462 static int search_fi(void *elt1, void *elt2)
464 RBSR_FINDEX *f1 = (RBSR_FINDEX *) elt1;
465 RBSR_FINDEX *f2 = (RBSR_FINDEX *) elt2;
467 if (f1->findex == (f2->findex - 1)) {
470 } else if (f1->findex2 == (f2->findex2 + 1)) {
473 } else if (f1->findex >= f2->findex && f1->findex2 <= f2->findex2) {
477 return (f1->findex > f2->findex) ? 1 : -1;
480 rblist *create_bsr_list(uint32_t JobId, int findex, int findex2)
483 RBSR_FINDEX *fi = NULL;
484 rblist *bsr_list = New(rblist(bsr, &bsr->link));
489 bsr_list->insert(bsr, search_rbsr);
493 fi->findex2 = findex2;
495 bsr->fi_list->insert(fi, search_fi);
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.
506 * When doing restore from tree, FileIndex are not sorted, so it can
509 void add_findex(rblist *bsr_list, uint32_t JobId, int32_t findex)
512 RBSR_FINDEX *fi, *nfi;
515 return; /* probably a dummy directory */
519 /* Walk down list of bsrs until we find the JobId */
520 bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
522 /* The list is empty, or the JobId is not already in,
528 bsr_list->insert(bsr, search_rbsr);
535 fi = bsr->m_fi = new_findex();
539 fi->findex2 = findex;
541 Dmsg1(1000, "Trying to insert %ld\n", findex);
542 /* try to insert our fi */
543 nfi = (RBSR_FINDEX*) bsr->fi_list->insert((void *)fi, search_fi);
545 /* We found an existing one, extend it */
547 if (findex == (nfi->findex2 + 1)) {
548 Dmsg2(1000, "Extend %ld-%ld\n", nfi->findex, findex);
549 nfi->findex2 = findex;
551 } else if (findex == (nfi->findex - 1)) {
552 Dmsg2(1000, "Extend %ld-%ld\n", findex, nfi->findex2);
553 nfi->findex = findex;
556 Dmsg2(1000, "Found the same values? %ld-%ld\n", nfi->findex, nfi->findex2);
560 Dmsg2(1000, "Inserted %ld-%ld\n", fi->findex, fi->findex2);
561 bsr->m_fi = NULL; /* comsumed */
566 * Add all possible FileIndexes to the list of BootStrap records.
567 * Here we are only dealing with JobId's and the FileIndexes
568 * associated with those JobIds.
570 void add_findex_all(rblist *bsr_list, uint32_t JobId, const char *fileregex)
576 /* Walk down list of bsrs until we find the JobId */
577 bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
579 if (!bsr) { /* Must add new JobId */
582 fi->findex2 = INT32_MAX;
586 bsr->fi_list->insert(fi, search_fi);
587 bsr_list->insert(bsr, search_rbsr);
590 /* If we use regexp to restore, set it for each jobid */
591 bsr->fileregex = bstrdup(fileregex);
597 * At this point, bsr points to bsr containing this JobId,
601 fi->findex2 = INT32_MAX;
602 bsr->fi_list->insert(fi, search_fi);
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
611 static int sendit(void *arg, int num_fields, char **row)
613 JCR *jcr = (JCR *)arg;
615 if (job_canceled(jcr)) {
619 if (row[2][0] == '0') { /* discard when file_index == 0 */
623 /* sending with checksum */
625 && row[6][0] /* skip checksum = '0' */
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]);
631 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
632 row[0], row[1], 0, row[4], 0, 0, row[5]);
638 /* We list all files for a given FI structure */
639 static void scan_findex(JCR *jcr, RBSR *bsr,
640 int32_t FirstIndex, int32_t LastIndex,
641 int32_t &lastFileIndex, uint32_t &lastJobId)
645 memset(&fdbr, 0, sizeof(fdbr));
647 fi = (RBSR_FINDEX *) bsr->fi_list->first();
649 int32_t findex, findex2;
651 /* fi points to the first item of the list, or the next item that is not
652 * contigous to the previous group
655 findex2 = fi->findex2;
657 /* Sometime (with the restore command for example), the fi_list can
658 * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop
659 * is here to merge blocks and reduce the bsr output. The next while(fi)
660 * iteration will use the next_fi that points to the last merged element.
662 RBSR_FINDEX *next_fi;
663 for (next_fi = (RBSR_FINDEX*) bsr->fi_list->next(fi);
664 next_fi && next_fi->findex == (findex2+1);
665 next_fi = (RBSR_FINDEX *) bsr->fi_list->next(next_fi))
667 findex2 = next_fi->findex2;
670 /* next_fi points after the current block (or to the end of the list), so
671 * the next while() iteration will use the next value
675 /* We look if the current FI block match the volume information */
676 if ((findex >= FirstIndex && findex <= LastIndex) ||
677 (findex2 >= FirstIndex && findex2 <= LastIndex) ||
678 (findex < FirstIndex && findex2 > LastIndex)) {
680 findex = findex < FirstIndex ? FirstIndex : findex;
681 findex2 = findex2 > LastIndex ? LastIndex : findex2;
684 /* Display only new files */
685 if (findex != lastFileIndex || bsr->JobId != lastJobId) {
686 /* Not the same file, or not the same job */
687 fdbr.FileIndex = findex;
690 } else if (findex2 != lastFileIndex) {
691 /* We are in the same job, and the first index was already generated */
692 fdbr.FileIndex = findex + 1;
696 /* Keep the current values for the next loop */
697 lastJobId = bsr->JobId;
698 lastFileIndex = findex2;
700 /* Generate if needed the list of files */
702 fdbr.FileIndex2 = findex2;
703 fdbr.JobId = bsr->JobId;
704 /* New code not working */
705 //db_list_files(jcr, jcr->db, &fdbr, sendit, jcr);
712 * Scan bsr data for a single bsr record
714 static void scan_bsr_item(JCR *jcr, RBSR *bsr)
716 int32_t lastFileIndex=0;
717 uint32_t lastJobId=0;
719 * For a given volume, loop over all the JobMedia records.
720 * VolCount is the number of JobMedia records.
722 for (int i=0; i < bsr->VolCount; i++) {
723 if (!is_volume_selected(bsr->fi_list,
724 bsr->VolParams[i].FirstIndex,
725 bsr->VolParams[i].LastIndex))
730 scan_findex(jcr, bsr,
731 bsr->VolParams[i].FirstIndex,
732 bsr->VolParams[i].LastIndex,
733 lastFileIndex, lastJobId);
738 * We need to find all files from the BSR. All files are listed, this is used
739 * to send the list of the files to be restored to a plugin for example.
741 void scan_bsr(JCR *jcr)
746 if (!jcr->JobIds || *jcr->JobIds == 0) {
747 foreach_rblist(bsr, jcr->bsr_list) {
748 scan_bsr_item(jcr, bsr);
752 for (p=jcr->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
753 foreach_rblist(bsr, jcr->bsr_list) {
754 if (JobId == bsr->JobId) {
755 scan_bsr_item(jcr, bsr);