]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
More migration cleanup
[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    Bacula® - The Network Backup Solution
14
15    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
16
17    The main author of Bacula is Kern Sibbald, with contributions from
18    many others, a complete list can be found in the file AUTHORS.
19    This program is Free Software; you can redistribute it and/or
20    modify it under the terms of version two of the GNU General Public
21    License as published by the Free Software Foundation plus additions
22    that are listed in the file LICENSE.
23
24    This program is distributed in the hope that it will be useful, but
25    WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27    General Public License for more details.
28
29    You should have received a copy of the GNU General Public License
30    along with this program; if not, write to the Free Software
31    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32    02110-1301, USA.
33
34    Bacula® is a registered trademark of John Walker.
35    The licensor of Bacula is the Free Software Foundation Europe
36    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37    Switzerland, email:ftf@fsfeurope.org.
38 */
39
40 #include "bacula.h"
41 #include "dird.h"
42
43 /* Forward referenced functions */
44 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
45 void print_bsr(UAContext *ua, RBSR *bsr);
46
47
48 /*
49  * Create new FileIndex entry for BSR
50  */
51 RBSR_FINDEX *new_findex()
52 {
53    RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
54    memset(fi, 0, sizeof(RBSR_FINDEX));
55    return fi;
56 }
57
58 /* Free all BSR FileIndex entries */
59 static void free_findex(RBSR_FINDEX *fi)
60 {
61    RBSR_FINDEX *next;
62    for ( ; fi; fi=next) {
63       next = fi->next;
64       free(fi);
65    }
66 }
67
68 /* 
69  * Get storage device name from Storage resource
70  */
71 static bool get_storage_device(char *device, char *storage)
72 {
73    STORE *store;
74    if (storage[0] == 0) {
75       return false;
76    }
77    store = (STORE *)GetResWithName(R_STORAGE, storage);    
78    if (!store) {
79       return false;
80    }
81    DEVICE *dev = (DEVICE *)(store->device->first());
82    if (!dev) {
83       return false;
84    }
85    bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
86    return true;
87 }
88
89 /*
90  * Our data structures were not designed completely
91  *  correctly, so the file indexes cover the full
92  *  range regardless of volume. The FirstIndex and LastIndex
93  *  passed in here are for the current volume, so when
94  *  writing out the fi, constrain them to those values.
95  *
96  * We are called here once for each JobMedia record
97  *  for each Volume.
98  */
99 static uint32_t write_findex(RBSR_FINDEX *fi,
100               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
101 {
102    uint32_t count = 0;
103    for ( ; fi; fi=fi->next) {
104       int32_t findex, findex2;
105       if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
106           (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
107           (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
108          findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
109          findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
110          if (findex == findex2) {
111             fprintf(fd, "FileIndex=%d\n", findex);
112             count++;
113          } else {
114             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
115             count += findex2 - findex + 1;
116          }
117       }
118    }
119    return count;
120 }
121
122 /*
123  * Find out if Volume defined with FirstIndex and LastIndex
124  *   falls within the range of selected files in the bsr.
125  */
126 static bool is_volume_selected(RBSR_FINDEX *fi,
127               int32_t FirstIndex, int32_t LastIndex)
128 {
129    for ( ; fi; fi=fi->next) {
130       if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
131           (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
132           (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
133          return true;
134       }
135    }
136    return false;
137 }
138
139
140
141 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
142 {
143    bsendmsg(ua, "fi=0x%lx\n", fi);
144    for ( ; fi; fi=fi->next) {
145       if (fi->findex == fi->findex2) {
146          bsendmsg(ua, "FileIndex=%d\n", fi->findex);
147          Dmsg1(1000, "FileIndex=%d\n", fi->findex);
148       } else {
149          bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
150          Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
151       }
152    }
153 }
154
155 /* Create a new bootstrap record */
156 RBSR *new_bsr()
157 {
158    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
159    memset(bsr, 0, sizeof(RBSR));
160    return bsr;
161 }
162
163 /* Free the entire BSR */
164 void free_bsr(RBSR *bsr)
165 {
166    RBSR *next;
167    for ( ; bsr; bsr=next) {
168       free_findex(bsr->fi);
169       if (bsr->VolParams) {
170          free(bsr->VolParams);
171       }
172       next = bsr->next;
173       free(bsr);
174    }
175 }
176
177 /*
178  * Complete the BSR by filling in the VolumeName and
179  *  VolSessionId and VolSessionTime using the JobId
180  */
181 bool complete_bsr(UAContext *ua, RBSR *bsr)
182 {
183    for ( ; bsr; bsr=bsr->next) {
184       JOB_DBR jr;
185       memset(&jr, 0, sizeof(jr));
186       jr.JobId = bsr->JobId;
187       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
188          bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
189          return false;
190       }
191       bsr->VolSessionId = jr.VolSessionId;
192       bsr->VolSessionTime = jr.VolSessionTime;
193       if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
194            &(bsr->VolParams))) == 0) {
195          bsendmsg(ua, _("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
196          if (bsr->VolParams) {
197             free(bsr->VolParams);
198             bsr->VolParams = NULL;
199          }
200          return false;
201       }
202    }
203    return true;
204 }
205
206 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
207 static uint32_t uniq = 0;
208
209 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
210 {
211    JCR *jcr = ua->jcr;
212    int i = find_arg_with_value(ua, "bootstrap");
213    if (i >= 0) {
214       Mmsg(fname, "%s", ua->argv[i]);              
215       jcr->unlink_bsr = false;
216    } else {
217       P(mutex);
218       uniq++;
219       V(mutex);
220       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
221       jcr->unlink_bsr = true;
222    }
223    if (jcr->RestoreBootstrap) {
224       free(jcr->RestoreBootstrap);
225    }
226    jcr->RestoreBootstrap = bstrdup(fname.c_str());
227 }
228
229 /*
230  * Write the bootstrap records to file
231  */
232 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
233 {
234    FILE *fd;
235    POOL_MEM fname(PM_MESSAGE);
236    POOL_MEM volmsg(PM_MESSAGE);
237    uint32_t count = 0;;
238    bool err;
239    char *p;
240    JobId_t JobId;
241    char Device[MAX_NAME_LENGTH];
242
243    make_unique_restore_filename(ua, fname);
244    fd = fopen(fname.c_str(), "w+b");
245    if (!fd) {
246       berrno be;
247       bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
248          fname.c_str(), be.strerror());
249       goto bail_out;
250    }
251    /* Write them to file */
252    count = write_bsr(ua, rx, fd);
253    err = ferror(fd);
254    fclose(fd);
255    if (count == 0) {
256       bsendmsg(ua, _("No files found to restore/migrate. No bootstrap file written.\n"));
257       goto bail_out;
258    }
259    if (err) {
260       bsendmsg(ua, _("Error writing bsr file.\n"));
261       count = 0;
262       goto bail_out;
263    }
264
265
266    bsendmsg(ua, _("Bootstrap records written to %s\n"), fname.c_str());
267
268    /* Tell the user what he will need to mount */
269    bsendmsg(ua, "\n");
270    bsendmsg(ua, _("The job will require the following\n"
271                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
272                   "===========================================================================\n"));
273    /* Create Unique list of Volumes using prompt list */
274    start_prompt(ua, "");
275    if (*rx.JobIds) {
276       /* Ensure that the volumes are printed in JobId order */
277       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
278          for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
279             if (JobId != nbsr->JobId) {
280                continue;
281             }
282             for (int i=0; i < nbsr->VolCount; i++) {
283                if (nbsr->VolParams[i].VolumeName[0]) {
284                   if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
285                      Device[0] = 0;
286                   }
287                   Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
288                        nbsr->VolParams[i].VolumeName, 
289                        nbsr->VolParams[i].Storage, Device);
290                   add_prompt(ua, volmsg.c_str());
291                }
292             }
293          }
294       }
295    } else {
296       /* Print Volumes in any order */
297       for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
298          for (int i=0; i < nbsr->VolCount; i++) {
299             if (nbsr->VolParams[i].VolumeName[0]) {
300                if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
301                   Device[0] = 0;
302                }
303                Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
304                     nbsr->VolParams[i].VolumeName, 
305                     nbsr->VolParams[i].Storage, Device);
306                add_prompt(ua, volmsg.c_str());
307             }
308          }
309       }
310    }
311    for (int i=0; i < ua->num_prompts; i++) {
312       bsendmsg(ua, "   %s\n", ua->prompt[i]);
313       free(ua->prompt[i]);
314    }
315    if (ua->num_prompts == 0) {
316       bsendmsg(ua, _("No Volumes found to restore.\n"));
317       count = 0;
318    }
319    ua->num_prompts = 0;
320    bsendmsg(ua, "\n");
321
322 bail_out:
323    return count;
324 }
325
326 /*
327  * Here we actually write out the details of the bsr file.
328  *  Note, there is one bsr for each JobId, but the bsr may
329  *  have multiple volumes, which have been entered in the
330  *  order they were written.  
331  * The bsrs must be written out in the order the JobIds
332  *  are found in the jobid list.
333  */
334 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
335 {
336    uint32_t count = 0;
337    uint32_t total_count = 0;
338    uint32_t LastIndex = 0;
339    bool first = true;
340    char *p;
341    JobId_t JobId;
342    char device[MAX_NAME_LENGTH];
343    RBSR *bsr;
344    if (*rx.JobIds == 0) {
345       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
346          /*
347           * For a given volume, loop over all the JobMedia records.
348           *   VolCount is the number of JobMedia records.
349           */
350          for (int i=0; i < bsr->VolCount; i++) {
351             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
352                  bsr->VolParams[i].LastIndex)) {
353                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
354                continue;
355             }
356             if (!rx.store) {
357                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
358                                              bsr->VolParams[i].MediaType);
359             }
360             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
361             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
362             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
363                fprintf(fd, "Device=\"%s\"\n", device);
364             }
365             if (bsr->VolParams[i].Slot > 0) {
366                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
367             }
368             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
369             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
370             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
371                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
372             } else {
373                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
374                        bsr->VolParams[i].EndFile);
375             }
376             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
377                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
378             } else {
379                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
380                        bsr->VolParams[i].EndBlock);
381             }
382    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
383    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
384
385             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
386                                  bsr->VolParams[i].LastIndex, fd);
387             if (count) {
388                fprintf(fd, "Count=%u\n", count);
389             }
390             total_count += count;
391             /* If the same file is present on two tapes or in two files
392              *   on a tape, it is a continuation, and should not be treated
393              *   twice in the totals.
394              */
395             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
396                total_count--;
397             }
398             first = false;
399             LastIndex = bsr->VolParams[i].LastIndex;
400          }
401       }
402       return total_count;
403    }
404    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
405       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
406          if (JobId != bsr->JobId) {
407             continue;
408          }
409          /*
410           * For a given volume, loop over all the JobMedia records.
411           *   VolCount is the number of JobMedia records.
412           */
413          for (int i=0; i < bsr->VolCount; i++) {
414             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
415                  bsr->VolParams[i].LastIndex)) {
416                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
417                continue;
418             }
419             if (!rx.store) {
420                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
421                                              bsr->VolParams[i].MediaType);
422             }
423             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
424             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
425             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
426                fprintf(fd, "Device=\"%s\"\n", device);
427             }
428             if (bsr->VolParams[i].Slot > 0) {
429                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
430             }
431             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
432             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
433             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
434                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
435             } else {
436                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
437                        bsr->VolParams[i].EndFile);
438             }
439             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
440                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
441             } else {
442                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
443                        bsr->VolParams[i].EndBlock);
444             }
445    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
446    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
447
448             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
449                                  bsr->VolParams[i].LastIndex, fd);
450             if (count) {
451                fprintf(fd, "Count=%u\n", count);
452             }
453             total_count += count;
454             /* If the same file is present on two tapes or in two files
455              *   on a tape, it is a continuation, and should not be treated
456              *   twice in the totals.
457              */
458             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
459                total_count--;
460             }
461             first = false;
462             LastIndex = bsr->VolParams[i].LastIndex;
463          }
464       }
465    }
466    return total_count;
467 }
468
469 void print_bsr(UAContext *ua, RBSR *bsr)
470 {
471    for ( ; bsr; bsr=bsr->next) {
472       for (int i=0; i < bsr->VolCount; i++) {
473          bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
474          bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
475          bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
476          bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
477          bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
478                   bsr->VolParams[i].EndFile);
479          bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
480                   bsr->VolParams[i].EndBlock);
481          print_findex(ua, bsr->fi);
482       }
483       print_bsr(ua, bsr->next);
484    }
485 }
486
487
488
489
490 /*
491  * Add a FileIndex to the list of BootStrap records.
492  *  Here we are only dealing with JobId's and the FileIndexes
493  *  associated with those JobIds.
494  */
495 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
496 {
497    RBSR *nbsr;
498    RBSR_FINDEX *fi, *lfi;
499
500    if (findex == 0) {
501       return;                         /* probably a dummy directory */
502    }
503
504    if (bsr->fi == NULL) {             /* if no FI add one */
505       /* This is the first FileIndex item in the chain */
506       bsr->fi = new_findex();
507       bsr->JobId = JobId;
508       bsr->fi->findex = findex;
509       bsr->fi->findex2 = findex;
510       return;
511    }
512    /* Walk down list of bsrs until we find the JobId */
513    if (bsr->JobId != JobId) {
514       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
515          if (nbsr->JobId == JobId) {
516             bsr = nbsr;
517             break;
518          }
519       }
520
521       if (!nbsr) {                    /* Must add new JobId */
522          /* Add new JobId at end of chain */
523          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
524             {  }
525          nbsr->next = new_bsr();
526          nbsr->next->JobId = JobId;
527          nbsr->next->fi = new_findex();
528          nbsr->next->fi->findex = findex;
529          nbsr->next->fi->findex2 = findex;
530          return;
531       }
532    }
533
534    /*
535     * At this point, bsr points to bsr containing this JobId,
536     *  and we are sure that there is at least one fi record.
537     */
538    lfi = fi = bsr->fi;
539    /* Check if this findex is smaller than first item */
540    if (findex < fi->findex) {
541       if ((findex+1) == fi->findex) {
542          fi->findex = findex;         /* extend down */
543          return;
544       }
545       fi = new_findex();              /* yes, insert before first item */
546       fi->findex = findex;
547       fi->findex2 = findex;
548       fi->next = lfi;
549       bsr->fi = fi;
550       return;
551    }
552    /* Walk down fi chain and find where to insert insert new FileIndex */
553    for ( ; fi; fi=fi->next) {
554       if (findex == (fi->findex2 + 1)) {  /* extend up */
555          RBSR_FINDEX *nfi;
556          fi->findex2 = findex;
557          /*
558           * If the following record contains one higher, merge its
559           *   file index by extending it up.
560           */
561          if (fi->next && ((findex+1) == fi->next->findex)) {
562             nfi = fi->next;
563             fi->findex2 = nfi->findex2;
564             fi->next = nfi->next;
565             free(nfi);
566          }
567          return;
568       }
569       if (findex < fi->findex) {      /* add before */
570          if ((findex+1) == fi->findex) {
571             fi->findex = findex;
572             return;
573          }
574          break;
575       }
576       lfi = fi;
577    }
578    /* Add to last place found */
579    fi = new_findex();
580    fi->findex = findex;
581    fi->findex2 = findex;
582    fi->next = lfi->next;
583    lfi->next = fi;
584    return;
585 }
586
587 /*
588  * Add all possible  FileIndexes to the list of BootStrap records.
589  *  Here we are only dealing with JobId's and the FileIndexes
590  *  associated with those JobIds.
591  */
592 void add_findex_all(RBSR *bsr, uint32_t JobId)
593 {
594    RBSR *nbsr;
595    RBSR_FINDEX *fi;
596
597    if (bsr->fi == NULL) {             /* if no FI add one */
598       /* This is the first FileIndex item in the chain */
599       bsr->fi = new_findex();
600       bsr->JobId = JobId;
601       bsr->fi->findex = 1;
602       bsr->fi->findex2 = INT32_MAX;
603       return;
604    }
605    /* Walk down list of bsrs until we find the JobId */
606    if (bsr->JobId != JobId) {
607       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
608          if (nbsr->JobId == JobId) {
609             bsr = nbsr;
610             break;
611          }
612       }
613
614       if (!nbsr) {                    /* Must add new JobId */
615          /* Add new JobId at end of chain */
616          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
617             {  }
618          nbsr->next = new_bsr();
619          nbsr->next->JobId = JobId;
620          nbsr->next->fi = new_findex();
621          nbsr->next->fi->findex = 1;
622          nbsr->next->fi->findex2 = INT32_MAX;
623          return;
624       }
625    }
626
627    /*
628     * At this point, bsr points to bsr containing this JobId,
629     *  and we are sure that there is at least one fi record.
630     */
631    fi = bsr->fi;
632    fi->findex = 1;
633    fi->findex2 = INT32_MAX;
634    return;
635 }