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