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 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname)
197 int i = find_arg_with_value(ua, "bootstrap");
199 Mmsg(fname, "%s", ua->argv[i]);
200 jcr->unlink_bsr = false;
202 Mmsg(fname, "%s/%s.restore.%s.bsr", working_directory, my_name,
204 jcr->unlink_bsr = true;
209 * Write the bootstrap records to file
211 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
214 POOLMEM *fname = get_pool_memory(PM_MESSAGE);
220 make_unique_restore_filename(ua, &fname);
221 fd = fopen(fname, "w+");
224 bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
225 fname, be.strerror());
228 /* Write them to file */
229 count = write_bsr(ua, rx, fd);
233 bsendmsg(ua, _("Error writing bsr file.\n"));
239 bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
241 /* Tell the user what he will need to mount */
243 bsendmsg(ua, _("The job will require the following Volumes:\n"));
244 /* Create Unique list of Volumes using prompt list */
245 start_prompt(ua, "");
247 /* Ensure that the volumes are printed in JobId order */
248 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
249 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
250 if (JobId != nbsr->JobId) {
253 for (int i=0; i < nbsr->VolCount; i++) {
254 if (nbsr->VolParams[i].VolumeName[0]) {
255 add_prompt(ua, nbsr->VolParams[i].VolumeName);
261 /* Print Volumes in any order */
262 for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
263 for (int i=0; i < nbsr->VolCount; i++) {
264 if (nbsr->VolParams[i].VolumeName[0]) {
265 add_prompt(ua, nbsr->VolParams[i].VolumeName);
270 for (int i=0; i < ua->num_prompts; i++) {
271 bsendmsg(ua, " %s\n", ua->prompt[i]);
274 if (ua->num_prompts == 0) {
275 bsendmsg(ua, _("No Volumes found to restore.\n"));
282 free_pool_memory(fname);
287 * Here we actually write out the details of the bsr file.
288 * Note, there is one bsr for each JobId, but the bsr may
289 * have multiple volumes, which have been entered in the
290 * order they were written.
291 * The bsrs must be written out in the order the JobIds
292 * are found in the jobid list.
294 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
297 uint32_t total_count = 0;
298 uint32_t LastIndex = 0;
302 char device[MAX_NAME_LENGTH];
304 if (*rx.JobIds == 0) {
305 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
307 * For a given volume, loop over all the JobMedia records.
308 * VolCount is the number of JobMedia records.
310 for (int i=0; i < bsr->VolCount; i++) {
311 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
312 bsr->VolParams[i].LastIndex)) {
313 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
316 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
317 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
318 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
319 fprintf(fd, "Device=\"%s\"\n", device);
321 if (bsr->VolParams[i].Slot > 0) {
322 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
324 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
325 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
326 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
327 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
329 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
330 bsr->VolParams[i].EndFile);
332 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
333 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
335 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
336 bsr->VolParams[i].EndBlock);
338 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
339 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
341 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
342 bsr->VolParams[i].LastIndex, fd);
344 fprintf(fd, "Count=%u\n", count);
346 total_count += count;
347 /* If the same file is present on two tapes or in two files
348 * on a tape, it is a continuation, and should not be treated
349 * twice in the totals.
351 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
355 LastIndex = bsr->VolParams[i].LastIndex;
360 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
361 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
362 if (JobId != bsr->JobId) {
366 * For a given volume, loop over all the JobMedia records.
367 * VolCount is the number of JobMedia records.
369 for (int i=0; i < bsr->VolCount; i++) {
370 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
371 bsr->VolParams[i].LastIndex)) {
372 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
375 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
376 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
377 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
378 fprintf(fd, "Device=\"%s\"\n", device);
380 if (bsr->VolParams[i].Slot > 0) {
381 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
383 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
384 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
385 if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
386 fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
388 fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
389 bsr->VolParams[i].EndFile);
391 if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
392 fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
394 fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
395 bsr->VolParams[i].EndBlock);
397 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
398 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
400 count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
401 bsr->VolParams[i].LastIndex, fd);
403 fprintf(fd, "Count=%u\n", count);
405 total_count += count;
406 /* If the same file is present on two tapes or in two files
407 * on a tape, it is a continuation, and should not be treated
408 * twice in the totals.
410 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
414 LastIndex = bsr->VolParams[i].LastIndex;
421 void print_bsr(UAContext *ua, RBSR *bsr)
423 for ( ; bsr; bsr=bsr->next) {
424 for (int i=0; i < bsr->VolCount; i++) {
425 bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
426 bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
427 bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
428 bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
429 bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
430 bsr->VolParams[i].EndFile);
431 bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
432 bsr->VolParams[i].EndBlock);
433 print_findex(ua, bsr->fi);
435 print_bsr(ua, bsr->next);
443 * Add a FileIndex to the list of BootStrap records.
444 * Here we are only dealing with JobId's and the FileIndexes
445 * associated with those JobIds.
447 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
450 RBSR_FINDEX *fi, *lfi;
453 return; /* probably a dummy directory */
456 if (bsr->fi == NULL) { /* if no FI add one */
457 /* This is the first FileIndex item in the chain */
458 bsr->fi = new_findex();
460 bsr->fi->findex = findex;
461 bsr->fi->findex2 = findex;
464 /* Walk down list of bsrs until we find the JobId */
465 if (bsr->JobId != JobId) {
466 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
467 if (nbsr->JobId == JobId) {
473 if (!nbsr) { /* Must add new JobId */
474 /* Add new JobId at end of chain */
475 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
477 nbsr->next = new_bsr();
478 nbsr->next->JobId = JobId;
479 nbsr->next->fi = new_findex();
480 nbsr->next->fi->findex = findex;
481 nbsr->next->fi->findex2 = findex;
487 * At this point, bsr points to bsr containing this JobId,
488 * and we are sure that there is at least one fi record.
491 /* Check if this findex is smaller than first item */
492 if (findex < fi->findex) {
493 if ((findex+1) == fi->findex) {
494 fi->findex = findex; /* extend down */
497 fi = new_findex(); /* yes, insert before first item */
499 fi->findex2 = findex;
504 /* Walk down fi chain and find where to insert insert new FileIndex */
505 for ( ; fi; fi=fi->next) {
506 if (findex == (fi->findex2 + 1)) { /* extend up */
508 fi->findex2 = findex;
510 * If the following record contains one higher, merge its
511 * file index by extending it up.
513 if (fi->next && ((findex+1) == fi->next->findex)) {
515 fi->findex2 = nfi->findex2;
516 fi->next = nfi->next;
521 if (findex < fi->findex) { /* add before */
522 if ((findex+1) == fi->findex) {
530 /* Add to last place found */
533 fi->findex2 = findex;
534 fi->next = lfi->next;
540 * Add all possible FileIndexes to the list of BootStrap records.
541 * Here we are only dealing with JobId's and the FileIndexes
542 * associated with those JobIds.
544 void add_findex_all(RBSR *bsr, uint32_t JobId)
549 if (bsr->fi == NULL) { /* if no FI add one */
550 /* This is the first FileIndex item in the chain */
551 bsr->fi = new_findex();
554 bsr->fi->findex2 = INT32_MAX;
557 /* Walk down list of bsrs until we find the JobId */
558 if (bsr->JobId != JobId) {
559 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
560 if (nbsr->JobId == JobId) {
566 if (!nbsr) { /* Must add new JobId */
567 /* Add new JobId at end of chain */
568 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
570 nbsr->next = new_bsr();
571 nbsr->next->JobId = JobId;
572 nbsr->next->fi = new_findex();
573 nbsr->next->fi->findex = 1;
574 nbsr->next->fi->findex2 = INT32_MAX;
580 * At this point, bsr points to bsr containing this JobId,
581 * and we are sure that there is at least one fi record.
585 fi->findex2 = INT32_MAX;