]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / dird / bsr.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6
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.
9
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.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *   Bacula Director -- Bootstrap Record routines.
23  *
24  *      BSR (bootstrap record) handling routines split from
25  *        ua_restore.c July MMIII
26  *
27  *     Kern Sibbald, July MMII
28  *
29  */
30
31 #include "bacula.h"
32 #include "dird.h"
33
34 /* Forward referenced functions */
35 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
36
37 /*
38  * Create new FileIndex entry for BSR
39  */
40 RBSR_FINDEX *new_findex()
41 {
42    RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
43    memset(fi, 0, sizeof(RBSR_FINDEX));
44    return fi;
45 }
46
47 /* Free all BSR FileIndex entries */
48 static void free_findex(RBSR_FINDEX *fi)
49 {
50    RBSR_FINDEX *next;
51    for ( ; fi; fi=next) {
52       next = fi->next;
53       free(fi);
54    }
55 }
56
57 /* 
58  * Get storage device name from Storage resource
59  */
60 static bool get_storage_device(char *device, char *storage)
61 {
62    STORE *store;
63    if (storage[0] == 0) {
64       return false;
65    }
66    store = (STORE *)GetResWithName(R_STORAGE, storage);    
67    if (!store) {
68       return false;
69    }
70    DEVICE *dev = (DEVICE *)(store->device->first());
71    if (!dev) {
72       return false;
73    }
74    bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
75    return true;
76 }
77
78 /*
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.
84  *
85  * We are called here once for each JobMedia record
86  *  for each Volume.
87  */
88 static uint32_t write_findex(RBSR_FINDEX *fi,
89               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
90 {
91    uint32_t count = 0;
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);
101             count++;
102          } else {
103             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
104             count += findex2 - findex + 1;
105          }
106       }
107    }
108    return count;
109 }
110
111 /*
112  * Find out if Volume defined with FirstIndex and LastIndex
113  *   falls within the range of selected files in the bsr.
114  */
115 static bool is_volume_selected(RBSR_FINDEX *fi,
116               int32_t FirstIndex, int32_t LastIndex)
117 {
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)) {
122          return true;
123       }
124    }
125    return false;
126 }
127
128
129 /* Create a new bootstrap record */
130 RBSR *new_bsr()
131 {
132    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
133    memset(bsr, 0, sizeof(RBSR));
134    return bsr;
135 }
136
137 /* Free the entire BSR */
138 void free_bsr(RBSR *bsr)
139 {
140    RBSR *next;
141    for ( ; bsr; bsr=next) {
142       free_findex(bsr->fi);
143       if (bsr->VolParams) {
144          free(bsr->VolParams);
145       }
146       if (bsr->fileregex) {
147          free(bsr->fileregex);
148       }
149       next = bsr->next;
150       free(bsr);
151    }
152 }
153
154 /*
155  * Complete the BSR by filling in the VolumeName and
156  *  VolSessionId and VolSessionTime using the JobId
157  */
158 bool complete_bsr(UAContext *ua, RBSR *bsr)
159 {
160    for ( ; bsr; bsr=bsr->next) {
161       JOB_DBR jr;
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));
166          return false;
167       }
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 */
172          continue;
173       }
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;
180          }
181          return false;
182       }
183    }
184    return true;
185 }
186
187 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
188 static uint32_t uniq = 0;
189
190 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
191 {
192    JCR *jcr = ua->jcr;
193    int i = find_arg_with_value(ua, "bootstrap");
194    if (i >= 0) {
195       Mmsg(fname, "%s", ua->argv[i]);              
196       jcr->unlink_bsr = false;
197    } else {
198       P(mutex);
199       uniq++;
200       V(mutex);
201       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
202       jcr->unlink_bsr = true;
203    }
204    if (jcr->RestoreBootstrap) {
205       free(jcr->RestoreBootstrap);
206    }
207    jcr->RestoreBootstrap = bstrdup(fname.c_str());
208 }
209
210 /*
211  * Write the bootstrap records to file
212  */
213 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
214 {
215    FILE *fd;
216    POOL_MEM fname(PM_MESSAGE);
217    uint32_t count = 0;;
218    bool err;
219
220    make_unique_restore_filename(ua, fname);
221    fd = fopen(fname.c_str(), "w+b");
222    if (!fd) {
223       berrno be;
224       ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
225          fname.c_str(), be.bstrerror());
226       goto bail_out;
227    }
228    /* Write them to file */
229    count = write_bsr(ua, rx, fd);
230    err = ferror(fd);
231    fclose(fd);
232    if (count == 0) {
233       ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
234       goto bail_out;
235    }
236    if (err) {
237       ua->error_msg(_("Error writing bsr file.\n"));
238       count = 0;
239       goto bail_out;
240    }
241
242    ua->send_msg(_("Bootstrap records written to %s\n"), fname.c_str());
243
244    if (debug_level >= 10) {
245       print_bsr(ua, rx);
246    }
247
248 bail_out:
249    return count;
250 }
251
252 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
253 {
254    POOL_MEM volmsg(PM_MESSAGE);
255    char Device[MAX_NAME_LENGTH];
256    char online;
257    RBSR *bsr;
258
259    for (bsr=rx.bsr; bsr; bsr=bsr->next) {
260       if (JobId && JobId != bsr->JobId) {
261          continue;
262       }
263
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)) {
267                Device[0] = 0;
268             }
269             if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
270                online = '*';
271             } else {
272                online = ' ';
273             }
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());
278          }
279       }
280    }
281 }
282
283 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
284 {
285    char *p;
286    JobId_t JobId;
287
288    /* Tell the user what he will need to mount */
289    ua->send_msg("\n");
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);
298    } else {
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);
302       }
303    }
304    for (int i=0; i < ua->num_prompts; i++) {
305       ua->send_msg("   %s\n", ua->prompt[i]);
306       free(ua->prompt[i]);
307       if (ua->unique[i]) free(ua->unique[i]);
308    }
309    if (ua->num_prompts == 0) {
310       ua->send_msg(_("No Volumes found to restore.\n"));
311    } else {
312       ua->send_msg(_("\nVolumes marked with \"*\" are in the Autochanger.\n"));
313    }
314    ua->num_prompts = 0;
315    ua->send_msg("\n");
316
317    return;
318 }
319
320 /*
321  * Write bsr data for a single bsr record
322  */
323 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua, 
324                    RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
325 {
326    char ed1[50], ed2[50];
327    uint32_t count = 0;
328    uint32_t total_count = 0;
329    char device[MAX_NAME_LENGTH];
330
331    /*
332     * For a given volume, loop over all the JobMedia records.
333     *   VolCount is the number of JobMedia records.
334     */
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 */
339          continue;
340       }
341       if (!rx.store) {
342          find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
343                                        bsr->VolParams[i].MediaType);
344       }
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);
350       }
351       if (get_storage_device(device, bsr->VolParams[i].Storage)) {
352          fprintf(fd, "Device=\"%s\"\n", device);
353       }
354       if (bsr->VolParams[i].Slot > 0) {
355          fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
356       }
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);
363
364       count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
365                            bsr->VolParams[i].LastIndex, fd);
366       if (count) {
367          fprintf(fd, "Count=%u\n", count);
368       }
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.
373        */
374       if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
375          total_count--;
376       }
377       first = false;
378       LastIndex = bsr->VolParams[i].LastIndex;
379    }
380    return total_count;
381 }
382
383
384 /*
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.
391  */
392 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
393 {
394    bool first = true;
395    uint32_t LastIndex = 0;
396    uint32_t total_count = 0;
397    char *p;
398    JobId_t JobId;
399    RBSR *bsr;
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);
403       }
404       return total_count;
405    }
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);
410          }
411       }
412    }
413    return total_count;
414 }
415
416 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
417 {
418    write_bsr(ua, rx, stdout);
419 }
420
421
422
423
424 /*
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.
429  */
430 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
431 {
432    RBSR *nbsr;
433    RBSR_FINDEX *fi, *lfi;
434
435    if (findex == 0) {
436       return;                         /* probably a dummy directory */
437    }
438
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();
442       bsr->JobId = JobId;
443       bsr->fi->findex = findex;
444       bsr->fi->findex2 = findex;
445       return;
446    }
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) {
451             bsr = nbsr;
452             break;
453          }
454       }
455
456       if (!nbsr) {                    /* Must add new JobId */
457          /* Add new JobId at end of chain */
458          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
459             {  }
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;
465          return;
466       }
467    }
468
469    /*
470     * At this point, bsr points to bsr containing this JobId,
471     *  and we are sure that there is at least one fi record.
472     */
473    lfi = fi = bsr->fi;
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 */
478          return;
479       }
480       fi = new_findex();              /* yes, insert before first item */
481       fi->findex = findex;
482       fi->findex2 = findex;
483       fi->next = lfi;
484       bsr->fi = fi;
485       return;
486    }
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 */
490          RBSR_FINDEX *nfi;
491          fi->findex2 = findex;
492          /*
493           * If the following record contains one higher, merge its
494           *   file index by extending it up.
495           */
496          if (fi->next && ((findex+1) == fi->next->findex)) {
497             nfi = fi->next;
498             fi->findex2 = nfi->findex2;
499             fi->next = nfi->next;
500             free(nfi);
501          }
502          return;
503       }
504       if (findex < fi->findex) {      /* add before */
505          if ((findex+1) == fi->findex) {
506             fi->findex = findex;
507             return;
508          }
509          break;
510       }
511       lfi = fi;
512    }
513    /* Add to last place found */
514    fi = new_findex();
515    fi->findex = findex;
516    fi->findex2 = findex;
517    fi->next = lfi->next;
518    lfi->next = fi;
519    return;
520 }
521
522 /*
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.
526  */
527 void add_findex_all(RBSR *bsr, uint32_t JobId)
528 {
529    RBSR *nbsr;
530    RBSR_FINDEX *fi;
531
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();
535       bsr->JobId = JobId;
536       bsr->fi->findex = 1;
537       bsr->fi->findex2 = INT32_MAX;
538       return;
539    }
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) {
544             bsr = nbsr;
545             break;
546          }
547       }
548
549       if (!nbsr) {                    /* Must add new JobId */
550          /* Add new JobId at end of chain */
551          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
552             {  }
553
554          nbsr->next = new_bsr();
555          nbsr->next->JobId = JobId;
556
557          /* If we use regexp to restore, set it for each jobid */
558          if (bsr->fileregex) { 
559             nbsr->next->fileregex = bstrdup(bsr->fileregex);
560          }
561
562          nbsr->next->fi = new_findex();
563          nbsr->next->fi->findex = 1;
564          nbsr->next->fi->findex2 = INT32_MAX;
565          return;
566       }
567    }
568
569    /*
570     * At this point, bsr points to bsr containing this JobId,
571     *  and we are sure that there is at least one fi record.
572     */
573    fi = bsr->fi;
574    fi->findex = 1;
575    fi->findex2 = INT32_MAX;
576    return;
577 }