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-2005 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 * Our data structures were not designed completely
58 * correctly, so the file indexes cover the full
59 * range regardless of volume. The FirstIndex and LastIndex
60 * passed in here are for the current volume, so when
61 * writing out the fi, constrain them to those values.
63 * We are called here once for each JobMedia record
66 static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi,
67 int32_t FirstIndex, int32_t LastIndex, FILE *fd)
70 for ( ; fi; fi=fi->next) {
71 int32_t findex, findex2;
72 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
73 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
74 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
75 findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
76 findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
77 if (findex == findex2) {
78 fprintf(fd, "FileIndex=%d\n", findex);
81 fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
82 count += findex2 - findex + 1;
90 * Find out if Volume defined with FirstIndex and LastIndex
91 * falls within the range of selected files in the bsr.
93 static bool is_volume_selected(RBSR_FINDEX *fi,
94 int32_t FirstIndex, int32_t LastIndex)
96 for ( ; fi; fi=fi->next) {
97 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
98 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
99 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
108 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
110 bsendmsg(ua, "fi=0x%lx\n", fi);
111 for ( ; fi; fi=fi->next) {
112 if (fi->findex == fi->findex2) {
113 bsendmsg(ua, "FileIndex=%d\n", fi->findex);
114 Dmsg1(1000, "FileIndex=%d\n", fi->findex);
116 bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
117 Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
122 /* Create a new bootstrap record */
125 RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
126 memset(bsr, 0, sizeof(RBSR));
130 /* Free the entire BSR */
131 void free_bsr(RBSR *bsr)
134 for ( ; bsr; bsr=next) {
135 free_findex(bsr->fi);
136 if (bsr->VolParams) {
137 free(bsr->VolParams);
145 * Complete the BSR by filling in the VolumeName and
146 * VolSessionId and VolSessionTime using the JobId
148 bool complete_bsr(UAContext *ua, RBSR *bsr)
150 for ( ; bsr; bsr=bsr->next) {
152 memset(&jr, 0, sizeof(jr));
153 jr.JobId = bsr->JobId;
154 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
155 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
158 bsr->VolSessionId = jr.VolSessionId;
159 bsr->VolSessionTime = jr.VolSessionTime;
160 if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
161 &(bsr->VolParams))) == 0) {
162 bsendmsg(ua, _("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
163 if (bsr->VolParams) {
164 free(bsr->VolParams);
165 bsr->VolParams = NULL;
173 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname)
176 int i = find_arg_with_value(ua, "bootstrap");
178 Mmsg(fname, "%s", ua->argv[i]);
179 jcr->unlink_bsr = false;
181 Mmsg(fname, "%s/%s.restore.%s.bsr", working_directory, my_name,
183 jcr->unlink_bsr = true;
188 * Write the bootstrap records to file
190 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
193 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
199 make_unique_restore_filename(ua, &fname);
200 fd = fopen(fname, "w+");
203 bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
204 fname, be.strerror());
207 /* Write them to file */
208 count = write_bsr(ua, rx, fd);
212 bsendmsg(ua, _("Error writing bsr file.\n"));
218 bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
220 /* Tell the user what he will need to mount */
222 bsendmsg(ua, _("The job will require the following Volumes:\n"));
223 /* Create Unique list of Volumes using prompt list */
224 start_prompt(ua, "");
226 /* Ensure that the volumes are printed in JobId order */
227 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
228 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
229 if (JobId != nbsr->JobId) {
232 for (int i=0; i < nbsr->VolCount; i++) {
233 if (nbsr->VolParams[i].VolumeName[0]) {
234 add_prompt(ua, nbsr->VolParams[i].VolumeName);
240 /* Print Volumes in any order */
241 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
242 for (int i=0; i < nbsr->VolCount; i++) {
243 if (nbsr->VolParams[i].VolumeName[0]) {
244 add_prompt(ua, nbsr->VolParams[i].VolumeName);
249 for (int i=0; i < ua->num_prompts; i++) {
250 bsendmsg(ua, " %s\n", ua->prompt[i]);
253 if (ua->num_prompts == 0) {
254 bsendmsg(ua, _("No Volumes found to restore.\n"));
261 free_pool_memory(fname);
266 * Here we actually write out the details of the bsr file.
267 * Note, there is one bsr for each JobId, but the bsr may
268 * have multiple volumes, which have been entered in the
269 * order they were written.
270 * The bsrs must be written out in the order the JobIds
271 * are found in the jobid list.
273 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
276 uint32_t total_count = 0;
277 uint32_t LastIndex = 0;
282 if (*rx.JobIds == 0) {
283 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
285 * For a given volume, loop over all the JobMedia records.
286 * VolCount is the number of JobMedia records.
288 for (int i=0; i < bsr->VolCount; i++) {
289 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
290 bsr->VolParams[i].LastIndex)) {
291 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
294 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
295 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
296 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
297 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
298 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
299 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
301 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
302 bsr->VolParams[i].EndFile);
304 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
305 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
307 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
308 bsr->VolParams[i].EndBlock);
310 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
311 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
313 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
314 bsr->VolParams[i].LastIndex, fd);
316 fprintf(fd, "Count=%u\n", count);
318 total_count += count;
319 /* If the same file is present on two tapes or in two files
320 * on a tape, it is a continuation, and should not be treated
321 * twice in the totals.
323 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
327 LastIndex = bsr->VolParams[i].LastIndex;
332 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
333 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
334 if (JobId != bsr->JobId) {
338 * For a given volume, loop over all the JobMedia records.
339 * VolCount is the number of JobMedia records.
341 for (int i=0; i < bsr->VolCount; i++) {
342 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
343 bsr->VolParams[i].LastIndex)) {
344 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
347 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
348 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
349 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
350 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
351 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
352 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
354 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
355 bsr->VolParams[i].EndFile);
357 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
358 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
360 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
361 bsr->VolParams[i].EndBlock);
363 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
364 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
366 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
367 bsr->VolParams[i].LastIndex, fd);
369 fprintf(fd, "Count=%u\n", count);
371 total_count += count;
372 /* If the same file is present on two tapes or in two files
373 * on a tape, it is a continuation, and should not be treated
374 * twice in the totals.
376 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
380 LastIndex = bsr->VolParams[i].LastIndex;
387 void print_bsr(UAContext *ua, RBSR *bsr)
389 for ( ; bsr; bsr=bsr->next) {
390 for (int i=0; i < bsr->VolCount; i++) {
391 bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
392 bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
393 bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
394 bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
395 bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
396 bsr->VolParams[i].EndFile);
397 bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
398 bsr->VolParams[i].EndBlock);
399 print_findex(ua, bsr->fi);
401 print_bsr(ua, bsr->next);
409 * Add a FileIndex to the list of BootStrap records.
410 * Here we are only dealing with JobId's and the FileIndexes
411 * associated with those JobIds.
413 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
416 RBSR_FINDEX *fi, *lfi;
419 return; /* probably a dummy directory */
422 if (bsr->fi == NULL) { /* if no FI add one */
423 /* This is the first FileIndex item in the chain */
424 bsr->fi = new_findex();
426 bsr->fi->findex = findex;
427 bsr->fi->findex2 = findex;
430 /* Walk down list of bsrs until we find the JobId */
431 if (bsr->JobId != JobId) {
432 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
433 if (nbsr->JobId == JobId) {
439 if (!nbsr) { /* Must add new JobId */
440 /* Add new JobId at end of chain */
441 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
443 nbsr->next = new_bsr();
444 nbsr->next->JobId = JobId;
445 nbsr->next->fi = new_findex();
446 nbsr->next->fi->findex = findex;
447 nbsr->next->fi->findex2 = findex;
453 * At this point, bsr points to bsr containing this JobId,
454 * and we are sure that there is at least one fi record.
457 /* Check if this findex is smaller than first item */
458 if (findex < fi->findex) {
459 if ((findex+1) == fi->findex) {
460 fi->findex = findex; /* extend down */
463 fi = new_findex(); /* yes, insert before first item */
465 fi->findex2 = findex;
470 /* Walk down fi chain and find where to insert insert new FileIndex */
471 for ( ; fi; fi=fi->next) {
472 if (findex == (fi->findex2 + 1)) { /* extend up */
474 fi->findex2 = findex;
476 * If the following record contains one higher, merge its
477 * file index by extending it up.
479 if (fi->next && ((findex+1) == fi->next->findex)) {
481 fi->findex2 = nfi->findex2;
482 fi->next = nfi->next;
487 if (findex < fi->findex) { /* add before */
488 if ((findex+1) == fi->findex) {
496 /* Add to last place found */
499 fi->findex2 = findex;
500 fi->next = lfi->next;
506 * Add all possible FileIndexes to the list of BootStrap records.
507 * Here we are only dealing with JobId's and the FileIndexes
508 * associated with those JobIds.
510 void add_findex_all(RBSR *bsr, uint32_t JobId)
515 if (bsr->fi == NULL) { /* if no FI add one */
516 /* This is the first FileIndex item in the chain */
517 bsr->fi = new_findex();
520 bsr->fi->findex2 = INT32_MAX;
523 /* Walk down list of bsrs until we find the JobId */
524 if (bsr->JobId != JobId) {
525 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
526 if (nbsr->JobId == JobId) {
532 if (!nbsr) { /* Must add new JobId */
533 /* Add new JobId at end of chain */
534 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
536 nbsr->next = new_bsr();
537 nbsr->next->JobId = JobId;
538 nbsr->next->fi = new_findex();
539 nbsr->next->fi->findex = 1;
540 nbsr->next->fi->findex2 = INT32_MAX;
546 * At this point, bsr points to bsr containing this JobId,
547 * and we are sure that there is at least one fi record.
551 fi->findex2 = INT32_MAX;