2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 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.
21 * Bacula Director -- Bootstrap Record routines.
23 * BSR (bootstrap record) handling routines split from
24 * ua_restore.c July MMIII
26 * Kern Sibbald, July MMII
33 /* Forward referenced functions */
34 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
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)) {
128 /* Create a new bootstrap record */
131 RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
132 memset(bsr, 0, sizeof(RBSR));
136 /* Free the entire BSR */
137 void free_bsr(RBSR *bsr)
140 for ( ; bsr; bsr=next) {
141 free_findex(bsr->fi);
142 if (bsr->VolParams) {
143 free(bsr->VolParams);
145 if (bsr->fileregex) {
146 free(bsr->fileregex);
154 * Complete the BSR by filling in the VolumeName and
155 * VolSessionId and VolSessionTime using the JobId
157 bool complete_bsr(UAContext *ua, RBSR *bsr)
159 for ( ; bsr; bsr=bsr->next) {
161 memset(&jr, 0, sizeof(jr));
162 jr.JobId = bsr->JobId;
163 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
164 ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
167 bsr->VolSessionId = jr.VolSessionId;
168 bsr->VolSessionTime = jr.VolSessionTime;
169 if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */
170 bsr->VolCount = 0; /* there are no volumes */
173 if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
174 &(bsr->VolParams))) == 0) {
175 ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
176 if (bsr->VolParams) {
177 free(bsr->VolParams);
178 bsr->VolParams = NULL;
186 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
187 static uint32_t uniq = 0;
189 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
192 int i = find_arg_with_value(ua, "bootstrap");
194 Mmsg(fname, "%s", ua->argv[i]);
195 jcr->unlink_bsr = false;
200 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
201 jcr->unlink_bsr = true;
203 if (jcr->RestoreBootstrap) {
204 free(jcr->RestoreBootstrap);
206 jcr->RestoreBootstrap = bstrdup(fname.c_str());
210 * Write the bootstrap records to file
212 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
215 POOL_MEM fname(PM_MESSAGE);
219 make_unique_restore_filename(ua, fname);
220 fd = fopen(fname.c_str(), "w+b");
223 ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
224 fname.c_str(), be.bstrerror());
227 /* Write them to file */
228 count = write_bsr(ua, rx, fd);
232 ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
236 ua->error_msg(_("Error writing bsr file.\n"));
241 ua->send_msg(_("Bootstrap records written to %s\n"), fname.c_str());
243 if (debug_level >= 10) {
251 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
253 POOL_MEM volmsg(PM_MESSAGE);
254 char Device[MAX_NAME_LENGTH];
258 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
259 if (JobId && JobId != bsr->JobId) {
263 for (int i=0; i < bsr->VolCount; i++) {
264 if (bsr->VolParams[i].VolumeName[0]) {
265 if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
268 if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
273 Mmsg(volmsg, "%c%-25s %-25s %-25s",
274 online, bsr->VolParams[i].VolumeName,
275 bsr->VolParams[i].Storage, Device);
276 add_prompt(ua, volmsg.c_str());
282 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
287 /* Tell the user what he will need to mount */
289 ua->send_msg(_("The Job will require the following (*=>InChanger):\n"
290 " Volume(s) Storage(s) SD Device(s)\n"
291 "===========================================================================\n"));
292 /* Create Unique list of Volumes using prompt list */
293 start_prompt(ua, "");
294 if (*rx.JobIds == 0) {
295 /* Print Volumes in any order */
296 display_vol_info(ua, rx, 0);
298 /* Ensure that the volumes are printed in JobId order */
299 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
300 display_vol_info(ua, rx, JobId);
303 for (int i=0; i < ua->num_prompts; i++) {
304 ua->send_msg(" %s\n", ua->prompt[i]);
306 if (ua->unique[i]) free(ua->unique[i]);
308 if (ua->num_prompts == 0) {
309 ua->send_msg(_("No Volumes found to restore.\n"));
311 ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
320 * Write bsr data for a single bsr record
322 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua,
323 RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
325 char ed1[50], ed2[50];
327 uint32_t total_count = 0;
328 char device[MAX_NAME_LENGTH];
331 * For a given volume, loop over all the JobMedia records.
332 * VolCount is the number of JobMedia records.
334 for (int i=0; i < bsr->VolCount; i++) {
335 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
336 bsr->VolParams[i].LastIndex)) {
337 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
341 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
342 bsr->VolParams[i].MediaType);
344 fprintf(fd, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
345 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
346 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
347 if (bsr->fileregex) {
348 fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
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 fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
359 edit_uint64(bsr->VolParams[i].EndAddr, ed2));
360 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
361 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
363 count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
364 bsr->VolParams[i].LastIndex, fd);
366 fprintf(fd, "Count=%u\n", count);
368 total_count += count;
369 /* If the same file is present on two tapes or in two files
370 * on a tape, it is a continuation, and should not be treated
371 * twice in the totals.
373 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
377 LastIndex = bsr->VolParams[i].LastIndex;
384 * Here we actually write out the details of the bsr file.
385 * Note, there is one bsr for each JobId, but the bsr may
386 * have multiple volumes, which have been entered in the
387 * order they were written.
388 * The bsrs must be written out in the order the JobIds
389 * are found in the jobid list.
391 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
394 uint32_t LastIndex = 0;
395 uint32_t total_count = 0;
399 if (*rx.JobIds == 0) {
400 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
401 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
405 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
406 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
407 if (JobId == bsr->JobId) {
408 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
415 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
417 write_bsr(ua, rx, stdout);
424 * Add a FileIndex to the list of BootStrap records.
425 * Here we are only dealing with JobId's and the FileIndexes
426 * associated with those JobIds.
427 * We expect that JobId, FileIndex are sorted ascending.
429 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
432 RBSR_FINDEX *fi, *lfi;
435 return; /* probably a dummy directory */
438 if (bsr->fi == NULL) { /* if no FI add one */
439 /* This is the first FileIndex item in the chain */
440 bsr->fi = new_findex();
442 bsr->fi->findex = findex;
443 bsr->fi->findex2 = findex;
446 /* Walk down list of bsrs until we find the JobId */
447 if (bsr->JobId != JobId) {
448 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
449 if (nbsr->JobId == JobId) {
455 if (!nbsr) { /* Must add new JobId */
456 /* Add new JobId at end of chain */
457 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
459 nbsr->next = new_bsr();
460 nbsr->next->JobId = JobId;
461 nbsr->next->fi = new_findex();
462 nbsr->next->fi->findex = findex;
463 nbsr->next->fi->findex2 = findex;
469 * At this point, bsr points to bsr containing this JobId,
470 * and we are sure that there is at least one fi record.
473 /* Check if this findex is smaller than first item */
474 if (findex < fi->findex) {
475 if ((findex+1) == fi->findex) {
476 fi->findex = findex; /* extend down */
479 fi = new_findex(); /* yes, insert before first item */
481 fi->findex2 = findex;
486 /* Walk down fi chain and find where to insert insert new FileIndex */
487 for ( ; fi; fi=fi->next) {
488 if (findex == (fi->findex2 + 1)) { /* extend up */
490 fi->findex2 = findex;
492 * If the following record contains one higher, merge its
493 * file index by extending it up.
495 if (fi->next && ((findex+1) == fi->next->findex)) {
497 fi->findex2 = nfi->findex2;
498 fi->next = nfi->next;
503 if (findex < fi->findex) { /* add before */
504 if ((findex+1) == fi->findex) {
512 /* Add to last place found */
515 fi->findex2 = findex;
516 fi->next = lfi->next;
522 * Add all possible FileIndexes to the list of BootStrap records.
523 * Here we are only dealing with JobId's and the FileIndexes
524 * associated with those JobIds.
526 void add_findex_all(RBSR *bsr, uint32_t JobId)
531 if (bsr->fi == NULL) { /* if no FI add one */
532 /* This is the first FileIndex item in the chain */
533 bsr->fi = new_findex();
536 bsr->fi->findex2 = INT32_MAX;
539 /* Walk down list of bsrs until we find the JobId */
540 if (bsr->JobId != JobId) {
541 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
542 if (nbsr->JobId == JobId) {
548 if (!nbsr) { /* Must add new JobId */
549 /* Add new JobId at end of chain */
550 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
553 nbsr->next = new_bsr();
554 nbsr->next->JobId = JobId;
556 /* If we use regexp to restore, set it for each jobid */
557 if (bsr->fileregex) {
558 nbsr->next->fileregex = bstrdup(bsr->fileregex);
561 nbsr->next->fi = new_findex();
562 nbsr->next->fi->findex = 1;
563 nbsr->next->fi->findex2 = INT32_MAX;
569 * At this point, bsr points to bsr containing this JobId,
570 * and we are sure that there is at least one fi record.
574 fi->findex2 = INT32_MAX;