]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
Fix out of order volumes during restore.
[bacula/bacula] / bacula / src / dird / bsr.c
1 /*
2  *
3  *   Bacula Director -- Bootstrap Record routines.
4  *
5  *      BSR (bootstrap record) handling routines split from
6  *        ua_restore.c July MMIII
7  *
8  *     Kern Sibbald, July MMII
9  *
10  *   Version $Id$
11  */
12
13 /*
14    Copyright (C) 2002-2005 Kern Sibbald
15
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.
20
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.
25
26  */
27
28 #include "bacula.h"
29 #include "dird.h"
30
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);
34
35
36 /*
37  * Create new FileIndex entry for BSR
38  */
39 RBSR_FINDEX *new_findex()
40 {
41    RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
42    memset(fi, 0, sizeof(RBSR_FINDEX));
43    return fi;
44 }
45
46 /* Free all BSR FileIndex entries */
47 static void free_findex(RBSR_FINDEX *fi)
48 {
49    RBSR_FINDEX *next;
50    for ( ; fi; fi=next) {
51       next = fi->next;
52       free(fi);
53    }
54 }
55
56 /*
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.
62  *
63  * We are called here once for each JobMedia record
64  *  for each Volume.
65  */
66 static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi,
67               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
68 {
69    uint32_t count = 0;
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);
79             count++;
80          } else {
81             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
82             count += findex2 - findex + 1;
83          }
84       }
85    }
86    return count;
87 }
88
89 /*
90  * Find out if Volume defined with FirstIndex and LastIndex
91  *   falls within the range of selected files in the bsr.
92  */
93 static bool is_volume_selected(RBSR_FINDEX *fi,
94               int32_t FirstIndex, int32_t LastIndex)
95 {
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)) {
100          return true;
101       }
102    }
103    return false;
104 }
105
106
107
108 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
109 {
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);
115       } else {
116          bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
117          Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
118       }
119    }
120 }
121
122 /* Create a new bootstrap record */
123 RBSR *new_bsr()
124 {
125    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
126    memset(bsr, 0, sizeof(RBSR));
127    return bsr;
128 }
129
130 /* Free the entire BSR */
131 void free_bsr(RBSR *bsr)
132 {
133    RBSR *next;
134    for ( ; bsr; bsr=next) {
135       free_findex(bsr->fi);
136       if (bsr->VolParams) {
137          free(bsr->VolParams);
138       }
139       next = bsr->next;
140       free(bsr);
141    }
142 }
143
144 /*
145  * Complete the BSR by filling in the VolumeName and
146  *  VolSessionId and VolSessionTime using the JobId
147  */
148 bool complete_bsr(UAContext *ua, RBSR *bsr)
149 {
150    for ( ; bsr; bsr=bsr->next) {
151       JOB_DBR jr;
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));
156          return false;
157       }
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;
166          }
167          return false;
168       }
169    }
170    return true;
171 }
172
173 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname)
174 {
175    JCR *jcr = ua->jcr;
176    int i = find_arg_with_value(ua, "bootstrap");
177    if (i >= 0) {
178       Mmsg(fname, "%s", ua->argv[i]);              
179       jcr->unlink_bsr = false;
180    } else {
181       Mmsg(fname, "%s/%s.restore.%s.bsr", working_directory, my_name, 
182          jcr->Job);
183       jcr->unlink_bsr = true;
184    }
185 }
186
187 /*
188  * Write the bootstrap records to file
189  */
190 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
191 {
192    FILE *fd;
193    POOLMEM *fname = get_pool_memory(PM_MESSAGE);
194    uint32_t count = 0;;
195    bool err;
196    char *p;
197    JobId_t JobId;
198
199    make_unique_restore_filename(ua, &fname);
200    fd = fopen(fname, "w+");
201    if (!fd) {
202       berrno be;
203       bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
204          fname, be.strerror());
205       goto bail_out;
206    }
207    /* Write them to file */
208    count = write_bsr(ua, rx, fd);
209    err = ferror(fd);
210    fclose(fd);
211    if (err) {
212       bsendmsg(ua, _("Error writing bsr file.\n"));
213       count = 0;
214       goto bail_out;
215    }
216
217
218    bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
219
220    /* Tell the user what he will need to mount */
221    bsendmsg(ua, "\n");
222    bsendmsg(ua, _("The job will require the following Volumes:\n"));
223    /* Create Unique list of Volumes using prompt list */
224    start_prompt(ua, "");
225    if (*rx.JobIds) {
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) {
230                continue;
231             }
232             for (int i=0; i < nbsr->VolCount; i++) {
233                if (nbsr->VolParams[i].VolumeName[0]) {
234                   add_prompt(ua, nbsr->VolParams[i].VolumeName);
235                }
236             }
237          }
238       }
239    } else {
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);
245             }
246          }
247       }
248    }
249    for (int i=0; i < ua->num_prompts; i++) {
250       bsendmsg(ua, "   %s\n", ua->prompt[i]);
251       free(ua->prompt[i]);
252    }
253    if (ua->num_prompts == 0) {
254       bsendmsg(ua, _("No Volumes found to restore.\n"));
255       count = 0;
256    }
257    ua->num_prompts = 0;
258    bsendmsg(ua, "\n");
259
260 bail_out:
261    free_pool_memory(fname);
262    return count;
263 }
264
265 /*
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.
272  */
273 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
274 {
275    uint32_t count = 0;
276    uint32_t total_count = 0;
277    uint32_t LastIndex = 0;
278    bool first = true;
279    char *p;
280    JobId_t JobId;
281    RBSR *bsr;
282    if (*rx.JobIds == 0) {
283       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
284          /*
285           * For a given volume, loop over all the JobMedia records.
286           *   VolCount is the number of JobMedia records.
287           */
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 */
292                continue;
293             }
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);
300             } else {
301                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
302                        bsr->VolParams[i].EndFile);
303             }
304             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
305                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
306             } else {
307                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
308                        bsr->VolParams[i].EndBlock);
309             }
310    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
311    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
312
313             count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
314                                  bsr->VolParams[i].LastIndex, fd);
315             if (count) {
316                fprintf(fd, "Count=%u\n", count);
317             }
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.
322              */
323             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
324                total_count--;
325             }
326             first = false;
327             LastIndex = bsr->VolParams[i].LastIndex;
328          }
329       }
330       return total_count;
331    }
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) {
335             continue;
336          }
337          /*
338           * For a given volume, loop over all the JobMedia records.
339           *   VolCount is the number of JobMedia records.
340           */
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 */
345                continue;
346             }
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);
353             } else {
354                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
355                        bsr->VolParams[i].EndFile);
356             }
357             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
358                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
359             } else {
360                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
361                        bsr->VolParams[i].EndBlock);
362             }
363    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
364    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
365
366             count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
367                                  bsr->VolParams[i].LastIndex, fd);
368             if (count) {
369                fprintf(fd, "Count=%u\n", count);
370             }
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.
375              */
376             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
377                total_count--;
378             }
379             first = false;
380             LastIndex = bsr->VolParams[i].LastIndex;
381          }
382       }
383    }
384    return total_count;
385 }
386
387 void print_bsr(UAContext *ua, RBSR *bsr)
388 {
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);
400       }
401       print_bsr(ua, bsr->next);
402    }
403 }
404
405
406
407
408 /*
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.
412  */
413 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
414 {
415    RBSR *nbsr;
416    RBSR_FINDEX *fi, *lfi;
417
418    if (findex == 0) {
419       return;                         /* probably a dummy directory */
420    }
421
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();
425       bsr->JobId = JobId;
426       bsr->fi->findex = findex;
427       bsr->fi->findex2 = findex;
428       return;
429    }
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) {
434             bsr = nbsr;
435             break;
436          }
437       }
438
439       if (!nbsr) {                    /* Must add new JobId */
440          /* Add new JobId at end of chain */
441          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
442             {  }
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;
448          return;
449       }
450    }
451
452    /*
453     * At this point, bsr points to bsr containing this JobId,
454     *  and we are sure that there is at least one fi record.
455     */
456    lfi = fi = bsr->fi;
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 */
461          return;
462       }
463       fi = new_findex();              /* yes, insert before first item */
464       fi->findex = findex;
465       fi->findex2 = findex;
466       fi->next = lfi;
467       bsr->fi = fi;
468       return;
469    }
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 */
473          RBSR_FINDEX *nfi;
474          fi->findex2 = findex;
475          /*
476           * If the following record contains one higher, merge its
477           *   file index by extending it up.
478           */
479          if (fi->next && ((findex+1) == fi->next->findex)) {
480             nfi = fi->next;
481             fi->findex2 = nfi->findex2;
482             fi->next = nfi->next;
483             free(nfi);
484          }
485          return;
486       }
487       if (findex < fi->findex) {      /* add before */
488          if ((findex+1) == fi->findex) {
489             fi->findex = findex;
490             return;
491          }
492          break;
493       }
494       lfi = fi;
495    }
496    /* Add to last place found */
497    fi = new_findex();
498    fi->findex = findex;
499    fi->findex2 = findex;
500    fi->next = lfi->next;
501    lfi->next = fi;
502    return;
503 }
504
505 /*
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.
509  */
510 void add_findex_all(RBSR *bsr, uint32_t JobId)
511 {
512    RBSR *nbsr;
513    RBSR_FINDEX *fi;
514
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();
518       bsr->JobId = JobId;
519       bsr->fi->findex = 1;
520       bsr->fi->findex2 = INT32_MAX;
521       return;
522    }
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) {
527             bsr = nbsr;
528             break;
529          }
530       }
531
532       if (!nbsr) {                    /* Must add new JobId */
533          /* Add new JobId at end of chain */
534          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
535             {  }
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;
541          return;
542       }
543    }
544
545    /*
546     * At this point, bsr points to bsr containing this JobId,
547     *  and we are sure that there is at least one fi record.
548     */
549    fi = bsr->fi;
550    fi->findex = 1;
551    fi->findex2 = INT32_MAX;
552    return;
553 }