3 * Bacula Director -- Bootstrap Record routines.
5 * BSR (bootstrap record) handling routines split from
6 * ua_restore.c July MMIII
8 * Kern Sibbald, July MMII
14 Copyright (C) 2002-2006 Kern Sibbald
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU General Public License
18 version 2 as amended with additional clauses defined in the
19 file LICENSE in the main source directory.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 the file LICENSE for additional details.
31 /* Forward referenced functions */
32 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
33 void print_bsr(UAContext *ua, RBSR *bsr);
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));
46 /* Free all BSR FileIndex entries */
47 static void free_findex(RBSR_FINDEX *fi)
50 for ( ; fi; fi=next) {
57 * Get storage device name from Storage resource
59 static bool get_storage_device(char *device, char *storage)
62 if (storage[0] == 0) {
65 store = (STORE *)GetResWithName(R_STORAGE, storage);
69 DEVICE *dev = (DEVICE *)(store->device->first());
73 bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
78 * Our data structures were not designed completely
79 * correctly, so the file indexes cover the full
80 * range regardless of volume. The FirstIndex and LastIndex
81 * passed in here are for the current volume, so when
82 * writing out the fi, constrain them to those values.
84 * We are called here once for each JobMedia record
87 static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi,
88 int32_t FirstIndex, int32_t LastIndex, FILE *fd)
91 for ( ; fi; fi=fi->next) {
92 int32_t findex, findex2;
93 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
94 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
95 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
96 findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
97 findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
98 if (findex == findex2) {
99 fprintf(fd, "FileIndex=%d\n", findex);
102 fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
103 count += findex2 - findex + 1;
111 * Find out if Volume defined with FirstIndex and LastIndex
112 * falls within the range of selected files in the bsr.
114 static bool is_volume_selected(RBSR_FINDEX *fi,
115 int32_t FirstIndex, int32_t LastIndex)
117 for ( ; fi; fi=fi->next) {
118 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
119 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
120 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
129 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
131 bsendmsg(ua, "fi=0x%lx\n", fi);
132 for ( ; fi; fi=fi->next) {
133 if (fi->findex == fi->findex2) {
134 bsendmsg(ua, "FileIndex=%d\n", fi->findex);
135 Dmsg1(1000, "FileIndex=%d\n", fi->findex);
137 bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
138 Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
143 /* Create a new bootstrap record */
146 RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
147 memset(bsr, 0, sizeof(RBSR));
151 /* Free the entire BSR */
152 void free_bsr(RBSR *bsr)
155 for ( ; bsr; bsr=next) {
156 free_findex(bsr->fi);
157 if (bsr->VolParams) {
158 free(bsr->VolParams);
166 * Complete the BSR by filling in the VolumeName and
167 * VolSessionId and VolSessionTime using the JobId
169 bool complete_bsr(UAContext *ua, RBSR *bsr)
171 for ( ; bsr; bsr=bsr->next) {
173 memset(&jr, 0, sizeof(jr));
174 jr.JobId = bsr->JobId;
175 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
176 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
179 bsr->VolSessionId = jr.VolSessionId;
180 bsr->VolSessionTime = jr.VolSessionTime;
181 if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
182 &(bsr->VolParams))) == 0) {
183 bsendmsg(ua, _("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
184 if (bsr->VolParams) {
185 free(bsr->VolParams);
186 bsr->VolParams = NULL;
194 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
195 static uint32_t uniq = 0;
197 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname)
200 int i = find_arg_with_value(ua, "bootstrap");
202 Mmsg(fname, "%s", ua->argv[i]);
203 jcr->unlink_bsr = false;
208 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
209 jcr->unlink_bsr = true;
211 if (jcr->RestoreBootstrap) {
212 free(jcr->RestoreBootstrap);
214 jcr->RestoreBootstrap = bstrdup(*fname);
218 * Write the bootstrap records to file
220 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
223 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
229 make_unique_restore_filename(ua, &fname);
230 fd = fopen(fname, "w+");
233 bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
234 fname, be.strerror());
237 /* Write them to file */
238 count = write_bsr(ua, rx, fd);
242 bsendmsg(ua, _("Error writing bsr file.\n"));
248 bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
250 /* Tell the user what he will need to mount */
252 bsendmsg(ua, _("The job will require the following Volumes:\n"));
253 /* Create Unique list of Volumes using prompt list */
254 start_prompt(ua, "");
256 /* Ensure that the volumes are printed in JobId order */
257 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
258 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
259 if (JobId != nbsr->JobId) {
262 for (int i=0; i < nbsr->VolCount; i++) {
263 if (nbsr->VolParams[i].VolumeName[0]) {
264 add_prompt(ua, nbsr->VolParams[i].VolumeName);
270 /* Print Volumes in any order */
271 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
272 for (int i=0; i < nbsr->VolCount; i++) {
273 if (nbsr->VolParams[i].VolumeName[0]) {
274 add_prompt(ua, nbsr->VolParams[i].VolumeName);
279 for (int i=0; i < ua->num_prompts; i++) {
280 bsendmsg(ua, " %s\n", ua->prompt[i]);
283 if (ua->num_prompts == 0) {
284 bsendmsg(ua, _("No Volumes found to restore.\n"));
291 free_pool_memory(fname);
296 * Here we actually write out the details of the bsr file.
297 * Note, there is one bsr for each JobId, but the bsr may
298 * have multiple volumes, which have been entered in the
299 * order they were written.
300 * The bsrs must be written out in the order the JobIds
301 * are found in the jobid list.
303 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
306 uint32_t total_count = 0;
307 uint32_t LastIndex = 0;
311 char device[MAX_NAME_LENGTH];
313 if (*rx.JobIds == 0) {
314 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
316 * For a given volume, loop over all the JobMedia records.
317 * VolCount is the number of JobMedia records.
319 for (int i=0; i < bsr->VolCount; i++) {
320 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
321 bsr->VolParams[i].LastIndex)) {
322 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
325 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
326 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
327 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
328 fprintf(fd, "Device=\"%s\"\n", device);
330 if (bsr->VolParams[i].Slot > 0) {
331 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
333 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
334 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
335 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
336 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
338 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
339 bsr->VolParams[i].EndFile);
341 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
342 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
344 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
345 bsr->VolParams[i].EndBlock);
347 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
348 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
350 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
351 bsr->VolParams[i].LastIndex, fd);
353 fprintf(fd, "Count=%u\n", count);
355 total_count += count;
356 /* If the same file is present on two tapes or in two files
357 * on a tape, it is a continuation, and should not be treated
358 * twice in the totals.
360 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
364 LastIndex = bsr->VolParams[i].LastIndex;
369 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
370 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
371 if (JobId != bsr->JobId) {
375 * For a given volume, loop over all the JobMedia records.
376 * VolCount is the number of JobMedia records.
378 for (int i=0; i < bsr->VolCount; i++) {
379 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
380 bsr->VolParams[i].LastIndex)) {
381 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
384 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
385 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
386 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
387 fprintf(fd, "Device=\"%s\"\n", device);
389 if (bsr->VolParams[i].Slot > 0) {
390 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
392 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
393 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
394 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
395 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
397 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
398 bsr->VolParams[i].EndFile);
400 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
401 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
403 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
404 bsr->VolParams[i].EndBlock);
406 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
407 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
409 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
410 bsr->VolParams[i].LastIndex, fd);
412 fprintf(fd, "Count=%u\n", count);
414 total_count += count;
415 /* If the same file is present on two tapes or in two files
416 * on a tape, it is a continuation, and should not be treated
417 * twice in the totals.
419 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
423 LastIndex = bsr->VolParams[i].LastIndex;
430 void print_bsr(UAContext *ua, RBSR *bsr)
432 for ( ; bsr; bsr=bsr->next) {
433 for (int i=0; i < bsr->VolCount; i++) {
434 bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
435 bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
436 bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
437 bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
438 bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
439 bsr->VolParams[i].EndFile);
440 bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
441 bsr->VolParams[i].EndBlock);
442 print_findex(ua, bsr->fi);
444 print_bsr(ua, bsr->next);
452 * Add a FileIndex to the list of BootStrap records.
453 * Here we are only dealing with JobId's and the FileIndexes
454 * associated with those JobIds.
456 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
459 RBSR_FINDEX *fi, *lfi;
462 return; /* probably a dummy directory */
465 if (bsr->fi == NULL) { /* if no FI add one */
466 /* This is the first FileIndex item in the chain */
467 bsr->fi = new_findex();
469 bsr->fi->findex = findex;
470 bsr->fi->findex2 = findex;
473 /* Walk down list of bsrs until we find the JobId */
474 if (bsr->JobId != JobId) {
475 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
476 if (nbsr->JobId == JobId) {
482 if (!nbsr) { /* Must add new JobId */
483 /* Add new JobId at end of chain */
484 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
486 nbsr->next = new_bsr();
487 nbsr->next->JobId = JobId;
488 nbsr->next->fi = new_findex();
489 nbsr->next->fi->findex = findex;
490 nbsr->next->fi->findex2 = findex;
496 * At this point, bsr points to bsr containing this JobId,
497 * and we are sure that there is at least one fi record.
500 /* Check if this findex is smaller than first item */
501 if (findex < fi->findex) {
502 if ((findex+1) == fi->findex) {
503 fi->findex = findex; /* extend down */
506 fi = new_findex(); /* yes, insert before first item */
508 fi->findex2 = findex;
513 /* Walk down fi chain and find where to insert insert new FileIndex */
514 for ( ; fi; fi=fi->next) {
515 if (findex == (fi->findex2 + 1)) { /* extend up */
517 fi->findex2 = findex;
519 * If the following record contains one higher, merge its
520 * file index by extending it up.
522 if (fi->next && ((findex+1) == fi->next->findex)) {
524 fi->findex2 = nfi->findex2;
525 fi->next = nfi->next;
530 if (findex < fi->findex) { /* add before */
531 if ((findex+1) == fi->findex) {
539 /* Add to last place found */
542 fi->findex2 = findex;
543 fi->next = lfi->next;
549 * Add all possible FileIndexes to the list of BootStrap records.
550 * Here we are only dealing with JobId's and the FileIndexes
551 * associated with those JobIds.
553 void add_findex_all(RBSR *bsr, uint32_t JobId)
558 if (bsr->fi == NULL) { /* if no FI add one */
559 /* This is the first FileIndex item in the chain */
560 bsr->fi = new_findex();
563 bsr->fi->findex2 = INT32_MAX;
566 /* Walk down list of bsrs until we find the JobId */
567 if (bsr->JobId != JobId) {
568 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
569 if (nbsr->JobId == JobId) {
575 if (!nbsr) { /* Must add new JobId */
576 /* Add new JobId at end of chain */
577 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
579 nbsr->next = new_bsr();
580 nbsr->next->JobId = JobId;
581 nbsr->next->fi = new_findex();
582 nbsr->next->fi->findex = 1;
583 nbsr->next->fi->findex2 = INT32_MAX;
589 * At this point, bsr points to bsr containing this JobId,
590 * and we are sure that there is at least one fi record.
594 fi->findex2 = INT32_MAX;