2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- Bootstrap Record routines.
24 * BSR (bootstrap record) handling routines split from
25 * ua_restore.c July MMIII
27 * Kern Sibbald, July MMII
34 /* Forward referenced functions */
35 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
38 * Create new FileIndex entry for BSR
40 RBSR_FINDEX *new_findex()
42 RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
43 memset(fi, 0, sizeof(RBSR_FINDEX));
47 /* Free all BSR FileIndex entries */
48 static void free_findex(RBSR_FINDEX *fi)
51 for ( ; fi; fi=next) {
58 * Get storage device name from Storage resource
60 static bool get_storage_device(char *device, char *storage)
63 if (storage[0] == 0) {
66 store = (STORE *)GetResWithName(R_STORAGE, storage);
70 DEVICE *dev = (DEVICE *)(store->device->first());
74 bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
79 * Our data structures were not designed completely
80 * correctly, so the file indexes cover the full
81 * range regardless of volume. The FirstIndex and LastIndex
82 * passed in here are for the current volume, so when
83 * writing out the fi, constrain them to those values.
85 * We are called here once for each JobMedia record
88 static uint32_t write_findex(RBSR_FINDEX *fi,
89 int32_t FirstIndex, int32_t LastIndex, FILE *fd)
92 for ( ; fi; fi=fi->next) {
93 int32_t findex, findex2;
94 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
95 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
96 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
97 findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
98 findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
99 if (findex == findex2) {
100 fprintf(fd, "FileIndex=%d\n", findex);
103 fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
104 count += findex2 - findex + 1;
112 * Find out if Volume defined with FirstIndex and LastIndex
113 * falls within the range of selected files in the bsr.
115 static bool is_volume_selected(RBSR_FINDEX *fi,
116 int32_t FirstIndex, int32_t LastIndex)
118 for ( ; fi; fi=fi->next) {
119 if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
120 (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
121 (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
129 /* Create a new bootstrap record */
132 RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
133 memset(bsr, 0, sizeof(RBSR));
137 /* Free the entire BSR */
138 void free_bsr(RBSR *bsr)
141 for ( ; bsr; bsr=next) {
142 free_findex(bsr->fi);
143 if (bsr->VolParams) {
144 free(bsr->VolParams);
146 if (bsr->fileregex) {
147 free(bsr->fileregex);
155 * Complete the BSR by filling in the VolumeName and
156 * VolSessionId and VolSessionTime using the JobId
158 bool complete_bsr(UAContext *ua, RBSR *bsr)
160 for ( ; bsr; bsr=bsr->next) {
162 memset(&jr, 0, sizeof(jr));
163 jr.JobId = bsr->JobId;
164 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
165 ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
168 bsr->VolSessionId = jr.VolSessionId;
169 bsr->VolSessionTime = jr.VolSessionTime;
170 if (jr.JobFiles == 0) { /* zero files is OK, not an error, but */
171 bsr->VolCount = 0; /* there are no volumes */
174 if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
175 &(bsr->VolParams))) == 0) {
176 ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
177 if (bsr->VolParams) {
178 free(bsr->VolParams);
179 bsr->VolParams = NULL;
187 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
188 static uint32_t uniq = 0;
190 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
193 int i = find_arg_with_value(ua, "bootstrap");
195 Mmsg(fname, "%s", ua->argv[i]);
196 jcr->unlink_bsr = false;
201 Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
202 jcr->unlink_bsr = true;
204 if (jcr->RestoreBootstrap) {
205 free(jcr->RestoreBootstrap);
207 jcr->RestoreBootstrap = bstrdup(fname.c_str());
211 * Write the bootstrap records to file
213 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
216 POOL_MEM fname(PM_MESSAGE);
220 make_unique_restore_filename(ua, fname);
221 fd = fopen(fname.c_str(), "w+b");
224 ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
225 fname.c_str(), be.bstrerror());
228 /* Write them to file */
229 count = write_bsr(ua, rx, fd);
233 ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
237 ua->error_msg(_("Error writing bsr file.\n"));
242 ua->send_msg(_("Bootstrap records written to %s\n"), fname.c_str());
244 if (debug_level >= 10) {
252 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
254 POOL_MEM volmsg(PM_MESSAGE);
255 char Device[MAX_NAME_LENGTH];
259 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
260 if (JobId && JobId != bsr->JobId) {
264 for (int i=0; i < bsr->VolCount; i++) {
265 if (bsr->VolParams[i].VolumeName[0]) {
266 if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
269 if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
274 Mmsg(volmsg, "%c%-25s %-25s %-25s",
275 online, bsr->VolParams[i].VolumeName,
276 bsr->VolParams[i].Storage, Device);
277 add_prompt(ua, volmsg.c_str());
283 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
288 /* Tell the user what he will need to mount */
290 ua->send_msg(_("The Job will require the following (*=>InChanger):\n"
291 " Volume(s) Storage(s) SD Device(s)\n"
292 "===========================================================================\n"));
293 /* Create Unique list of Volumes using prompt list */
294 start_prompt(ua, "");
295 if (*rx.JobIds == 0) {
296 /* Print Volumes in any order */
297 display_vol_info(ua, rx, 0);
299 /* Ensure that the volumes are printed in JobId order */
300 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
301 display_vol_info(ua, rx, JobId);
304 for (int i=0; i < ua->num_prompts; i++) {
305 ua->send_msg(" %s\n", ua->prompt[i]);
307 if (ua->unique[i]) free(ua->unique[i]);
309 if (ua->num_prompts == 0) {
310 ua->send_msg(_("No Volumes found to restore.\n"));
312 ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
321 * Write bsr data for a single bsr record
323 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua,
324 RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
326 char ed1[50], ed2[50];
328 uint32_t total_count = 0;
329 char device[MAX_NAME_LENGTH];
332 * For a given volume, loop over all the JobMedia records.
333 * VolCount is the number of JobMedia records.
335 for (int i=0; i < bsr->VolCount; i++) {
336 if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
337 bsr->VolParams[i].LastIndex)) {
338 bsr->VolParams[i].VolumeName[0] = 0; /* zap VolumeName */
342 find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
343 bsr->VolParams[i].MediaType);
345 fprintf(fd, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
346 fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
347 fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
348 if (bsr->fileregex) {
349 fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
351 if (get_storage_device(device, bsr->VolParams[i].Storage)) {
352 fprintf(fd, "Device=\"%s\"\n", device);
354 if (bsr->VolParams[i].Slot > 0) {
355 fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
357 fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
358 fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
359 fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
360 edit_uint64(bsr->VolParams[i].EndAddr, ed2));
361 // Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
362 // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
364 count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
365 bsr->VolParams[i].LastIndex, fd);
367 fprintf(fd, "Count=%u\n", count);
369 total_count += count;
370 /* If the same file is present on two tapes or in two files
371 * on a tape, it is a continuation, and should not be treated
372 * twice in the totals.
374 if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
378 LastIndex = bsr->VolParams[i].LastIndex;
385 * Here we actually write out the details of the bsr file.
386 * Note, there is one bsr for each JobId, but the bsr may
387 * have multiple volumes, which have been entered in the
388 * order they were written.
389 * The bsrs must be written out in the order the JobIds
390 * are found in the jobid list.
392 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
395 uint32_t LastIndex = 0;
396 uint32_t total_count = 0;
400 if (*rx.JobIds == 0) {
401 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
402 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
406 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
407 for (bsr=rx.bsr; bsr; bsr=bsr->next) {
408 if (JobId == bsr->JobId) {
409 total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
416 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
418 write_bsr(ua, rx, stdout);
425 * Add a FileIndex to the list of BootStrap records.
426 * Here we are only dealing with JobId's and the FileIndexes
427 * associated with those JobIds.
428 * We expect that JobId, FileIndex are sorted ascending.
430 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
433 RBSR_FINDEX *fi, *lfi;
436 return; /* probably a dummy directory */
439 if (bsr->fi == NULL) { /* if no FI add one */
440 /* This is the first FileIndex item in the chain */
441 bsr->fi = new_findex();
443 bsr->fi->findex = findex;
444 bsr->fi->findex2 = findex;
447 /* Walk down list of bsrs until we find the JobId */
448 if (bsr->JobId != JobId) {
449 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
450 if (nbsr->JobId == JobId) {
456 if (!nbsr) { /* Must add new JobId */
457 /* Add new JobId at end of chain */
458 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
460 nbsr->next = new_bsr();
461 nbsr->next->JobId = JobId;
462 nbsr->next->fi = new_findex();
463 nbsr->next->fi->findex = findex;
464 nbsr->next->fi->findex2 = findex;
470 * At this point, bsr points to bsr containing this JobId,
471 * and we are sure that there is at least one fi record.
474 /* Check if this findex is smaller than first item */
475 if (findex < fi->findex) {
476 if ((findex+1) == fi->findex) {
477 fi->findex = findex; /* extend down */
480 fi = new_findex(); /* yes, insert before first item */
482 fi->findex2 = findex;
487 /* Walk down fi chain and find where to insert insert new FileIndex */
488 for ( ; fi; fi=fi->next) {
489 if (findex == (fi->findex2 + 1)) { /* extend up */
491 fi->findex2 = findex;
493 * If the following record contains one higher, merge its
494 * file index by extending it up.
496 if (fi->next && ((findex+1) == fi->next->findex)) {
498 fi->findex2 = nfi->findex2;
499 fi->next = nfi->next;
504 if (findex < fi->findex) { /* add before */
505 if ((findex+1) == fi->findex) {
513 /* Add to last place found */
516 fi->findex2 = findex;
517 fi->next = lfi->next;
523 * Add all possible FileIndexes to the list of BootStrap records.
524 * Here we are only dealing with JobId's and the FileIndexes
525 * associated with those JobIds.
527 void add_findex_all(RBSR *bsr, uint32_t JobId)
532 if (bsr->fi == NULL) { /* if no FI add one */
533 /* This is the first FileIndex item in the chain */
534 bsr->fi = new_findex();
537 bsr->fi->findex2 = INT32_MAX;
540 /* Walk down list of bsrs until we find the JobId */
541 if (bsr->JobId != JobId) {
542 for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
543 if (nbsr->JobId == JobId) {
549 if (!nbsr) { /* Must add new JobId */
550 /* Add new JobId at end of chain */
551 for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
554 nbsr->next = new_bsr();
555 nbsr->next->JobId = JobId;
557 /* If we use regexp to restore, set it for each jobid */
558 if (bsr->fileregex) {
559 nbsr->next->fileregex = bstrdup(bsr->fileregex);
562 nbsr->next->fi = new_findex();
563 nbsr->next->fi->findex = 1;
564 nbsr->next->fi->findex2 = INT32_MAX;
570 * At this point, bsr points to bsr containing this JobId,
571 * and we are sure that there is at least one fi record.
575 fi->findex2 = INT32_MAX;