2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 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.
20 * Bacula Director -- Bootstrap Record routines.
22 * BSR (bootstrap record) handling routines split from
23 * ua_restore.c July MMIII
25 * Kern Sibbald, July MMII
31 /* Forward referenced functions */
32 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
35 * Create new FileIndex entry for BSR
37 RBSR_FINDEX *new_findex()
39 RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
40 memset(fi, 0, sizeof(RBSR_FINDEX));
45 * Get storage device name from Storage resource
47 static bool get_storage_device(char *device, char *storage)
50 if (storage[0] == 0) {
53 store = (STORE *)GetResWithName(R_STORAGE, storage);
57 DEVICE *dev = (DEVICE *)(store->device->first());
61 bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
66 * Our data structures were not designed completely
67 * correctly, so the file indexes cover the full
68 * range regardless of volume. The FirstIndex and LastIndex
69 * passed in here are for the current volume, so when
70 * writing out the fi, constrain them to those values.
72 * We are called here once for each JobMedia record
75 static uint32_t write_findex(rblist *fi_list,
76 int32_t FirstIndex, int32_t LastIndex, FILE *fd)
81 fi = (RBSR_FINDEX *) fi_list->first();
83 int32_t findex, findex2;
85 /* fi points to the first item of the list, or the next item that is not
86 * contigous to the previous group
89 findex2 = fi->findex2;
91 /* Sometime (with the restore command for example), the fi_list can
92 * contain false gaps (1-10, 11-11, 12-20 instead of 1-20). The for loop
93 * is here to merge blocks and reduce the bsr output. The next while(fi)
94 * iteration will use the next_fi that points to the last merged element.
97 for (next_fi = (RBSR_FINDEX*) fi_list->next(fi);
98 next_fi && next_fi->findex == (findex2+1);
99 next_fi = (RBSR_FINDEX *) fi_list->next(next_fi))
101 findex2 = next_fi->findex2;
104 /* next_fi points after the current block (or to the end of the list), so
105 * the next while() iteration will use the next value
109 /* We look if the current FI block match the volume information */
110 if ((findex >= FirstIndex && findex <= LastIndex) ||
111 (findex2 >= FirstIndex && findex2 <= LastIndex) ||
112 (findex < FirstIndex && findex2 > LastIndex)) {
114 findex = findex < FirstIndex ? FirstIndex : findex;
115 findex2 = findex2 > LastIndex ? LastIndex : findex2;
117 if (findex == findex2) {
118 fprintf(fd, "FileIndex=%d\n", findex);
121 fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
122 count += findex2 - findex + 1;
131 * Find out if Volume defined with FirstIndex and LastIndex
132 * falls within the range of selected files in the bsr.
134 static bool is_volume_selected(rblist *fi_list,
135 int32_t FirstIndex, int32_t LastIndex)
138 foreach_rblist(fi, fi_list) {
139 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
140 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
141 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
149 /* Create a new bootstrap record */
152 RBSR_FINDEX *fi=NULL;
153 RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
154 memset(bsr, 0, sizeof(RBSR));
155 bsr->fi_list = New(rblist(fi, &fi->link));
159 /* Free the entire BSR */
160 void free_bsr(rblist *bsr_list)
163 foreach_rblist(bsr, bsr_list) {
165 if (bsr->VolParams) {
166 free(bsr->VolParams);
168 if (bsr->fileregex) {
169 free(bsr->fileregex);
179 * Complete the BSR by filling in the VolumeName and
180 * VolSessionId and VolSessionTime using the JobId
182 bool complete_bsr(UAContext *ua, rblist *bsr_list)
185 foreach_rblist(bsr, bsr_list) {
187 memset(&jr, 0, sizeof(jr));
188 jr.JobId = bsr->JobId;
189 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
190 ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
193 bsr->VolSessionId = jr.VolSessionId;
194 bsr->VolSessionTime = jr.VolSessionTime;
195 if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */
196 bsr->VolCount = 0; /* there are no volumes */
199 if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
200 &(bsr->VolParams))) == 0) {
201 ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
202 if (bsr->VolParams) {
203 free(bsr->VolParams);
204 bsr->VolParams = NULL;
212 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
213 static uint32_t uniq = 0;
215 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
218 int i = find_arg_with_value(ua, "bootstrap");
220 Mmsg(fname, "%s", ua->argv[i]);
221 jcr->unlink_bsr = false;
226 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
227 jcr->unlink_bsr = true;
229 if (jcr->RestoreBootstrap) {
230 free(jcr->RestoreBootstrap);
232 jcr->RestoreBootstrap = bstrdup(fname.c_str());
236 * Write the bootstrap records to file
238 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
241 POOL_MEM fname(PM_MESSAGE);
245 make_unique_restore_filename(ua, fname);
246 fd = bfopen(fname.c_str(), "w+b");
249 ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
250 fname.c_str(), be.bstrerror());
253 /* Write them to file */
254 count = write_bsr(ua, rx, fd);
258 ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
262 ua->error_msg(_("Error writing bsr file.\n"));
267 if (chk_dbglvl(10)) {
275 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
277 POOL_MEM volmsg(PM_MESSAGE);
278 char Device[MAX_NAME_LENGTH];
282 foreach_rblist(bsr, rx.bsr_list) {
283 if (JobId && JobId != bsr->JobId) {
287 for (int i=0; i < bsr->VolCount; i++) {
288 if (bsr->VolParams[i].VolumeName[0]) {
289 if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
292 if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
297 Mmsg(volmsg, "%c%-25s %-25s %-25s",
298 online, bsr->VolParams[i].VolumeName,
299 bsr->VolParams[i].Storage, Device);
300 add_prompt(ua, volmsg.c_str());
306 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
311 /* Tell the user what he will need to mount */
313 ua->send_msg(_("The Job will require the following (*=>InChanger):\n"
314 " Volume(s) Storage(s) SD Device(s)\n"
315 "===========================================================================\n"));
316 /* Create Unique list of Volumes using prompt list */
317 start_prompt(ua, "");
318 if (*rx.JobIds == 0) {
319 /* Print Volumes in any order */
320 display_vol_info(ua, rx, 0);
322 /* Ensure that the volumes are printed in JobId order */
323 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
324 display_vol_info(ua, rx, JobId);
327 for (int i=0; i < ua->num_prompts; i++) {
328 ua->send_msg(" %s\n", ua->prompt[i]);
330 if (ua->unique[i]) free(ua->unique[i]);
332 if (ua->num_prompts == 0) {
333 ua->send_msg(_("No Volumes found to restore.\n"));
335 ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
344 * Write bsr data for a single bsr record
346 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua,
347 RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
349 char ed1[50], ed2[50];
351 uint32_t total_count = 0;
352 char device[MAX_NAME_LENGTH];
355 * For a given volume, loop over all the JobMedia records.
356 * VolCount is the number of JobMedia records.
358 for (int i=0; i < bsr->VolCount; i++) {
359 if (!is_volume_selected(bsr->fi_list, bsr->VolParams[i].FirstIndex,
360 bsr->VolParams[i].LastIndex)) {
361 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
365 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
366 bsr->VolParams[i].MediaType);
368 fprintf(fd, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
369 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
370 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
371 if (bsr->fileregex) {
372 fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
374 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
375 fprintf(fd, "Device=\"%s\"\n", device);
377 if (bsr->VolParams[i].Slot > 0) {
378 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
380 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
381 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
382 fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
383 edit_uint64(bsr->VolParams[i].EndAddr, ed2));
384 Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
385 bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
387 count = write_findex(bsr->fi_list, bsr->VolParams[i].FirstIndex,
388 bsr->VolParams[i].LastIndex, fd);
390 fprintf(fd, "Count=%u\n", count);
392 total_count += count;
393 /* If the same file is present on two tapes or in two files
394 * on a tape, it is a continuation, and should not be treated
395 * twice in the totals.
397 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
401 LastIndex = bsr->VolParams[i].LastIndex;
408 * Here we actually write out the details of the bsr file.
409 * Note, there is one bsr for each JobId, but the bsr may
410 * have multiple volumes, which have been entered in the
411 * order they were written.
412 * The bsrs must be written out in the order the JobIds
413 * are found in the jobid list.
415 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
418 uint32_t LastIndex = 0;
419 uint32_t total_count = 0;
423 if (*rx.JobIds == 0) {
424 foreach_rblist(bsr, rx.bsr_list) {
425 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
429 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
430 foreach_rblist(bsr, rx.bsr_list) {
431 if (JobId == bsr->JobId) {
432 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
439 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
441 write_bsr(ua, rx, stdout);
444 static int search_rbsr(void *elt1, void *elt2)
446 RBSR *bsr1 = (RBSR *)elt1;
447 RBSR *bsr = (RBSR *)elt2;
449 /* We might replace by a simple JobId - JobId */
450 if (bsr->JobId == bsr1->JobId) {
453 } else if (bsr->JobId < bsr1->JobId) {
460 static int search_fi(void *elt1, void *elt2)
462 RBSR_FINDEX *f1 = (RBSR_FINDEX *) elt1;
463 RBSR_FINDEX *f2 = (RBSR_FINDEX *) elt2;
465 if (f1->findex == (f2->findex - 1)) {
468 } else if (f1->findex2 == (f2->findex2 + 1)) {
471 } else if (f1->findex >= f2->findex && f1->findex2 <= f2->findex2) {
475 return (f1->findex > f2->findex) ? 1 : -1;
478 rblist *create_bsr_list(uint32_t JobId, int findex, int findex2)
481 RBSR_FINDEX *fi = NULL;
482 rblist *bsr_list = New(rblist(bsr, &bsr->link));
487 bsr_list->insert(bsr, search_rbsr);
491 fi->findex2 = findex2;
493 bsr->fi_list->insert(fi, search_fi);
499 * Add a FileIndex to the list of BootStrap records.
500 * Here we are only dealing with JobId's and the FileIndexes
501 * associated with those JobIds.
502 * We expect that JobId, FileIndex are sorted ascending.
504 * When doing restore from tree, FileIndex are not sorted, so it can
507 void add_findex(rblist *bsr_list, uint32_t JobId, int32_t findex)
510 RBSR_FINDEX *fi, *nfi;
513 return; /* probably a dummy directory */
517 /* Walk down list of bsrs until we find the JobId */
518 bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
520 /* The list is empty, or the JobId is not already in,
526 bsr_list->insert(bsr, search_rbsr);
533 fi = bsr->m_fi = new_findex();
537 fi->findex2 = findex;
539 Dmsg1(1000, "Trying to insert %ld\n", findex);
540 /* try to insert our fi */
541 nfi = (RBSR_FINDEX*) bsr->fi_list->insert((void *)fi, search_fi);
543 /* We found an existing one, extend it */
545 if (findex == (nfi->findex2 + 1)) {
546 Dmsg2(1000, "Extend %ld-%ld\n", nfi->findex, findex);
547 nfi->findex2 = findex;
549 } else if (findex == (nfi->findex - 1)) {
550 Dmsg2(1000, "Extend %ld-%ld\n", findex, nfi->findex2);
551 nfi->findex = findex;
554 Dmsg2(1000, "Found the same values? %ld-%ld\n", nfi->findex, nfi->findex2);
558 Dmsg2(1000, "Inserted %ld-%ld\n", fi->findex, fi->findex2);
559 bsr->m_fi = NULL; /* comsumed */
564 * Add all possible FileIndexes to the list of BootStrap records.
565 * Here we are only dealing with JobId's and the FileIndexes
566 * associated with those JobIds.
568 void add_findex_all(rblist *bsr_list, uint32_t JobId, const char *fileregex)
574 /* Walk down list of bsrs until we find the JobId */
575 bsr = (RBSR *)bsr_list->search(&bsr2, search_rbsr);
577 if (!bsr) { /* Must add new JobId */
580 fi->findex2 = INT32_MAX;
584 bsr->fi_list->insert(fi, search_fi);
585 bsr_list->insert(bsr, search_rbsr);
588 /* If we use regexp to restore, set it for each jobid */
589 bsr->fileregex = bstrdup(fileregex);
595 * At this point, bsr points to bsr containing this JobId,
599 fi->findex2 = INT32_MAX;
600 bsr->fi_list->insert(fi, search_fi);