]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
2321580aa26ba71086b43590a7c954c2b5b8475a
[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-2006 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  * Get storage device name from Storage resource
58  */
59 static bool get_storage_device(char *device, char *storage)
60 {
61    STORE *store;
62    if (storage[0] == 0) {
63       return false;
64    }
65    store = (STORE *)GetResWithName(R_STORAGE, storage);    
66    if (!store) {
67       return false;
68    }
69    DEVICE *dev = (DEVICE *)(store->device->first());
70    if (!dev) {
71       return false;
72    }
73    bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
74    return true;
75 }
76
77 /*
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.
83  *
84  * We are called here once for each JobMedia record
85  *  for each Volume.
86  */
87 static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi,
88               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
89 {
90    uint32_t count = 0;
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);
100             count++;
101          } else {
102             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
103             count += findex2 - findex + 1;
104          }
105       }
106    }
107    return count;
108 }
109
110 /*
111  * Find out if Volume defined with FirstIndex and LastIndex
112  *   falls within the range of selected files in the bsr.
113  */
114 static bool is_volume_selected(RBSR_FINDEX *fi,
115               int32_t FirstIndex, int32_t LastIndex)
116 {
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)) {
121          return true;
122       }
123    }
124    return false;
125 }
126
127
128
129 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
130 {
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);
136       } else {
137          bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
138          Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
139       }
140    }
141 }
142
143 /* Create a new bootstrap record */
144 RBSR *new_bsr()
145 {
146    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
147    memset(bsr, 0, sizeof(RBSR));
148    return bsr;
149 }
150
151 /* Free the entire BSR */
152 void free_bsr(RBSR *bsr)
153 {
154    RBSR *next;
155    for ( ; bsr; bsr=next) {
156       free_findex(bsr->fi);
157       if (bsr->VolParams) {
158          free(bsr->VolParams);
159       }
160       next = bsr->next;
161       free(bsr);
162    }
163 }
164
165 /*
166  * Complete the BSR by filling in the VolumeName and
167  *  VolSessionId and VolSessionTime using the JobId
168  */
169 bool complete_bsr(UAContext *ua, RBSR *bsr)
170 {
171    for ( ; bsr; bsr=bsr->next) {
172       JOB_DBR jr;
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));
177          return false;
178       }
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;
187          }
188          return false;
189       }
190    }
191    return true;
192 }
193
194 void make_unique_restore_filename(UAContext *ua, POOLMEM **fname)
195 {
196    JCR *jcr = ua->jcr;
197    int i = find_arg_with_value(ua, "bootstrap");
198    if (i >= 0) {
199       Mmsg(fname, "%s", ua->argv[i]);              
200       jcr->unlink_bsr = false;
201    } else {
202       Mmsg(fname, "%s/%s.restore.%s.bsr", working_directory, my_name, 
203          jcr->Job);
204       jcr->unlink_bsr = true;
205    }
206 }
207
208 /*
209  * Write the bootstrap records to file
210  */
211 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
212 {
213    FILE *fd;
214    POOLMEM *fname = get_pool_memory(PM_MESSAGE);
215    uint32_t count = 0;;
216    bool err;
217    char *p;
218    JobId_t JobId;
219
220    make_unique_restore_filename(ua, &fname);
221    fd = fopen(fname, "w+");
222    if (!fd) {
223       berrno be;
224       bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
225          fname, be.strerror());
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 (err) {
233       bsendmsg(ua, _("Error writing bsr file.\n"));
234       count = 0;
235       goto bail_out;
236    }
237
238
239    bsendmsg(ua, _("Bootstrap records written to %s\n"), fname);
240
241    /* Tell the user what he will need to mount */
242    bsendmsg(ua, "\n");
243    bsendmsg(ua, _("The job will require the following Volumes:\n"));
244    /* Create Unique list of Volumes using prompt list */
245    start_prompt(ua, "");
246    if (*rx.JobIds) {
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) {
251                continue;
252             }
253             for (int i=0; i < nbsr->VolCount; i++) {
254                if (nbsr->VolParams[i].VolumeName[0]) {
255                   add_prompt(ua, nbsr->VolParams[i].VolumeName);
256                }
257             }
258          }
259       }
260    } else {
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);
266             }
267          }
268       }
269    }
270    for (int i=0; i < ua->num_prompts; i++) {
271       bsendmsg(ua, "   %s\n", ua->prompt[i]);
272       free(ua->prompt[i]);
273    }
274    if (ua->num_prompts == 0) {
275       bsendmsg(ua, _("No Volumes found to restore.\n"));
276       count = 0;
277    }
278    ua->num_prompts = 0;
279    bsendmsg(ua, "\n");
280
281 bail_out:
282    free_pool_memory(fname);
283    return count;
284 }
285
286 /*
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.
293  */
294 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
295 {
296    uint32_t count = 0;
297    uint32_t total_count = 0;
298    uint32_t LastIndex = 0;
299    bool first = true;
300    char *p;
301    JobId_t JobId;
302    char device[MAX_NAME_LENGTH];
303    RBSR *bsr;
304    if (*rx.JobIds == 0) {
305       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
306          /*
307           * For a given volume, loop over all the JobMedia records.
308           *   VolCount is the number of JobMedia records.
309           */
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 */
314                continue;
315             }
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);
320             }
321             if (bsr->VolParams[i].Slot > 0) {
322                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
323             }
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);
328             } else {
329                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
330                        bsr->VolParams[i].EndFile);
331             }
332             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
333                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
334             } else {
335                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
336                        bsr->VolParams[i].EndBlock);
337             }
338    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
339    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
340
341             count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
342                                  bsr->VolParams[i].LastIndex, fd);
343             if (count) {
344                fprintf(fd, "Count=%u\n", count);
345             }
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.
350              */
351             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
352                total_count--;
353             }
354             first = false;
355             LastIndex = bsr->VolParams[i].LastIndex;
356          }
357       }
358       return total_count;
359    }
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) {
363             continue;
364          }
365          /*
366           * For a given volume, loop over all the JobMedia records.
367           *   VolCount is the number of JobMedia records.
368           */
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 */
373                continue;
374             }
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);
379             }
380             if (bsr->VolParams[i].Slot > 0) {
381                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
382             }
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);
387             } else {
388                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
389                        bsr->VolParams[i].EndFile);
390             }
391             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
392                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
393             } else {
394                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
395                        bsr->VolParams[i].EndBlock);
396             }
397    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
398    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
399
400             count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex,
401                                  bsr->VolParams[i].LastIndex, fd);
402             if (count) {
403                fprintf(fd, "Count=%u\n", count);
404             }
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.
409              */
410             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
411                total_count--;
412             }
413             first = false;
414             LastIndex = bsr->VolParams[i].LastIndex;
415          }
416       }
417    }
418    return total_count;
419 }
420
421 void print_bsr(UAContext *ua, RBSR *bsr)
422 {
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);
434       }
435       print_bsr(ua, bsr->next);
436    }
437 }
438
439
440
441
442 /*
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.
446  */
447 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
448 {
449    RBSR *nbsr;
450    RBSR_FINDEX *fi, *lfi;
451
452    if (findex == 0) {
453       return;                         /* probably a dummy directory */
454    }
455
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();
459       bsr->JobId = JobId;
460       bsr->fi->findex = findex;
461       bsr->fi->findex2 = findex;
462       return;
463    }
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) {
468             bsr = nbsr;
469             break;
470          }
471       }
472
473       if (!nbsr) {                    /* Must add new JobId */
474          /* Add new JobId at end of chain */
475          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
476             {  }
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;
482          return;
483       }
484    }
485
486    /*
487     * At this point, bsr points to bsr containing this JobId,
488     *  and we are sure that there is at least one fi record.
489     */
490    lfi = fi = bsr->fi;
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 */
495          return;
496       }
497       fi = new_findex();              /* yes, insert before first item */
498       fi->findex = findex;
499       fi->findex2 = findex;
500       fi->next = lfi;
501       bsr->fi = fi;
502       return;
503    }
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 */
507          RBSR_FINDEX *nfi;
508          fi->findex2 = findex;
509          /*
510           * If the following record contains one higher, merge its
511           *   file index by extending it up.
512           */
513          if (fi->next && ((findex+1) == fi->next->findex)) {
514             nfi = fi->next;
515             fi->findex2 = nfi->findex2;
516             fi->next = nfi->next;
517             free(nfi);
518          }
519          return;
520       }
521       if (findex < fi->findex) {      /* add before */
522          if ((findex+1) == fi->findex) {
523             fi->findex = findex;
524             return;
525          }
526          break;
527       }
528       lfi = fi;
529    }
530    /* Add to last place found */
531    fi = new_findex();
532    fi->findex = findex;
533    fi->findex2 = findex;
534    fi->next = lfi->next;
535    lfi->next = fi;
536    return;
537 }
538
539 /*
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.
543  */
544 void add_findex_all(RBSR *bsr, uint32_t JobId)
545 {
546    RBSR *nbsr;
547    RBSR_FINDEX *fi;
548
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();
552       bsr->JobId = JobId;
553       bsr->fi->findex = 1;
554       bsr->fi->findex2 = INT32_MAX;
555       return;
556    }
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) {
561             bsr = nbsr;
562             break;
563          }
564       }
565
566       if (!nbsr) {                    /* Must add new JobId */
567          /* Add new JobId at end of chain */
568          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
569             {  }
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;
575          return;
576       }
577    }
578
579    /*
580     * At this point, bsr points to bsr containing this JobId,
581     *  and we are sure that there is at least one fi record.
582     */
583    fi = bsr->fi;
584    fi->findex = 1;
585    fi->findex2 = INT32_MAX;
586    return;
587 }