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(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 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &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.c_str());
218 * Write the bootstrap records to file
220 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
223 POOL_MEM fname(PM_MESSAGE);
224 POOL_MEM volmsg(PM_MESSAGE);
229 char Device[MAX_NAME_LENGTH];
231 make_unique_restore_filename(ua, fname);
232 fd = fopen(fname.c_str(), "w+b");
235 bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
236 fname.c_str(), be.strerror());
239 /* Write them to file */
240 count = write_bsr(ua, rx, fd);
244 bsendmsg(ua, _("No files found to restore.\n"));
248 bsendmsg(ua, _("Error writing bsr file.\n"));
254 bsendmsg(ua, _("Bootstrap records written to %s\n"), fname.c_str());
256 /* Tell the user what he will need to mount */
258 bsendmsg(ua, _("The job will require the following\n"
259 " Volume(s) Storage(s) SD Device(s)\n"
260 "===========================================================================\n"));
261 /* Create Unique list of Volumes using prompt list */
262 start_prompt(ua, "");
264 /* Ensure that the volumes are printed in JobId order */
265 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
266 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
267 if (JobId != nbsr->JobId) {
270 for (int i=0; i < nbsr->VolCount; i++) {
271 if (nbsr->VolParams[i].VolumeName[0]) {
272 if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
275 Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s",
276 nbsr->VolParams[i].VolumeName,
277 nbsr->VolParams[i].Storage, Device);
278 add_prompt(ua, volmsg.c_str());
284 /* Print Volumes in any order */
285 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
286 for (int i=0; i < nbsr->VolCount; i++) {
287 if (nbsr->VolParams[i].VolumeName[0]) {
288 if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
291 Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s",
292 nbsr->VolParams[i].VolumeName,
293 nbsr->VolParams[i].Storage, Device);
294 add_prompt(ua, volmsg.c_str());
299 for (int i=0; i < ua->num_prompts; i++) {
300 bsendmsg(ua, " %s\n", ua->prompt[i]);
303 if (ua->num_prompts == 0) {
304 bsendmsg(ua, _("No Volumes found to restore.\n"));
315 * Here we actually write out the details of the bsr file.
316 * Note, there is one bsr for each JobId, but the bsr may
317 * have multiple volumes, which have been entered in the
318 * order they were written.
319 * The bsrs must be written out in the order the JobIds
320 * are found in the jobid list.
322 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
325 uint32_t total_count = 0;
326 uint32_t LastIndex = 0;
330 char device[MAX_NAME_LENGTH];
332 if (*rx.JobIds == 0) {
333 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
335 * For a given volume, loop over all the JobMedia records.
336 * VolCount is the number of JobMedia records.
338 for (int i=0; i < bsr->VolCount; i++) {
339 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
340 bsr->VolParams[i].LastIndex)) {
341 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
345 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
346 bsr->VolParams[i].MediaType);
348 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
349 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
350 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
351 fprintf(fd, "Device=\"%s\"\n", device);
353 if (bsr->VolParams[i].Slot > 0) {
354 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
356 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
357 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
358 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
359 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
361 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
362 bsr->VolParams[i].EndFile);
364 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
365 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
367 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
368 bsr->VolParams[i].EndBlock);
370 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
371 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
373 count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
374 bsr->VolParams[i].LastIndex, fd);
376 fprintf(fd, "Count=%u\n", count);
378 total_count += count;
379 /* If the same file is present on two tapes or in two files
380 * on a tape, it is a continuation, and should not be treated
381 * twice in the totals.
383 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
387 LastIndex = bsr->VolParams[i].LastIndex;
392 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
393 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
394 if (JobId != bsr->JobId) {
398 * For a given volume, loop over all the JobMedia records.
399 * VolCount is the number of JobMedia records.
401 for (int i=0; i < bsr->VolCount; i++) {
402 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
403 bsr->VolParams[i].LastIndex)) {
404 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
408 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
409 bsr->VolParams[i].MediaType);
411 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
412 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
413 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
414 fprintf(fd, "Device=\"%s\"\n", device);
416 if (bsr->VolParams[i].Slot > 0) {
417 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
419 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
420 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
421 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
422 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
424 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
425 bsr->VolParams[i].EndFile);
427 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
428 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
430 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
431 bsr->VolParams[i].EndBlock);
433 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
434 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
436 count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
437 bsr->VolParams[i].LastIndex, fd);
439 fprintf(fd, "Count=%u\n", count);
441 total_count += count;
442 /* If the same file is present on two tapes or in two files
443 * on a tape, it is a continuation, and should not be treated
444 * twice in the totals.
446 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
450 LastIndex = bsr->VolParams[i].LastIndex;
457 void print_bsr(UAContext *ua, RBSR *bsr)
459 for ( ; bsr; bsr=bsr->next) {
460 for (int i=0; i < bsr->VolCount; i++) {
461 bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
462 bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
463 bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
464 bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
465 bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
466 bsr->VolParams[i].EndFile);
467 bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
468 bsr->VolParams[i].EndBlock);
469 print_findex(ua, bsr->fi);
471 print_bsr(ua, bsr->next);
479 * Add a FileIndex to the list of BootStrap records.
480 * Here we are only dealing with JobId's and the FileIndexes
481 * associated with those JobIds.
483 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
486 RBSR_FINDEX *fi, *lfi;
489 return; /* probably a dummy directory */
492 if (bsr->fi == NULL) { /* if no FI add one */
493 /* This is the first FileIndex item in the chain */
494 bsr->fi = new_findex();
496 bsr->fi->findex = findex;
497 bsr->fi->findex2 = findex;
500 /* Walk down list of bsrs until we find the JobId */
501 if (bsr->JobId != JobId) {
502 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
503 if (nbsr->JobId == JobId) {
509 if (!nbsr) { /* Must add new JobId */
510 /* Add new JobId at end of chain */
511 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
513 nbsr->next = new_bsr();
514 nbsr->next->JobId = JobId;
515 nbsr->next->fi = new_findex();
516 nbsr->next->fi->findex = findex;
517 nbsr->next->fi->findex2 = findex;
523 * At this point, bsr points to bsr containing this JobId,
524 * and we are sure that there is at least one fi record.
527 /* Check if this findex is smaller than first item */
528 if (findex < fi->findex) {
529 if ((findex+1) == fi->findex) {
530 fi->findex = findex; /* extend down */
533 fi = new_findex(); /* yes, insert before first item */
535 fi->findex2 = findex;
540 /* Walk down fi chain and find where to insert insert new FileIndex */
541 for ( ; fi; fi=fi->next) {
542 if (findex == (fi->findex2 + 1)) { /* extend up */
544 fi->findex2 = findex;
546 * If the following record contains one higher, merge its
547 * file index by extending it up.
549 if (fi->next && ((findex+1) == fi->next->findex)) {
551 fi->findex2 = nfi->findex2;
552 fi->next = nfi->next;
557 if (findex < fi->findex) { /* add before */
558 if ((findex+1) == fi->findex) {
566 /* Add to last place found */
569 fi->findex2 = findex;
570 fi->next = lfi->next;
576 * Add all possible FileIndexes to the list of BootStrap records.
577 * Here we are only dealing with JobId's and the FileIndexes
578 * associated with those JobIds.
580 void add_findex_all(RBSR *bsr, uint32_t JobId)
585 if (bsr->fi == NULL) { /* if no FI add one */
586 /* This is the first FileIndex item in the chain */
587 bsr->fi = new_findex();
590 bsr->fi->findex2 = INT32_MAX;
593 /* Walk down list of bsrs until we find the JobId */
594 if (bsr->JobId != JobId) {
595 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
596 if (nbsr->JobId == JobId) {
602 if (!nbsr) { /* Must add new JobId */
603 /* Add new JobId at end of chain */
604 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
606 nbsr->next = new_bsr();
607 nbsr->next->JobId = JobId;
608 nbsr->next->fi = new_findex();
609 nbsr->next->fi->findex = 1;
610 nbsr->next->fi->findex2 = INT32_MAX;
616 * At this point, bsr points to bsr containing this JobId,
617 * and we are sure that there is at least one fi record.
621 fi->findex2 = INT32_MAX;