]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
28574282b987ec79fe90953c0476a6eddf0d6a71
[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
139 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
140 {
141    ua->send_msg("fi=0x%lx\n", fi);
142    for ( ; fi; fi=fi->next) {
143       if (fi->findex == fi->findex2) {
144          ua->send_msg("FileIndex=%d\n", fi->findex);
145          Dmsg1(1000, "FileIndex=%d\n", fi->findex);
146       } else {
147          ua->send_msg("FileIndex=%d-%d\n", fi->findex, fi->findex2);
148          Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
149       }
150    }
151 }
152
153 /* Create a new bootstrap record */
154 RBSR *new_bsr()
155 {
156    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
157    memset(bsr, 0, sizeof(RBSR));
158    return bsr;
159 }
160
161 /* Free the entire BSR */
162 void free_bsr(RBSR *bsr)
163 {
164    RBSR *next;
165    for ( ; bsr; bsr=next) {
166       free_findex(bsr->fi);
167       if (bsr->VolParams) {
168          free(bsr->VolParams);
169       }
170       next = bsr->next;
171       free(bsr);
172    }
173 }
174
175 /*
176  * Complete the BSR by filling in the VolumeName and
177  *  VolSessionId and VolSessionTime using the JobId
178  */
179 bool complete_bsr(UAContext *ua, RBSR *bsr)
180 {
181    for ( ; bsr; bsr=bsr->next) {
182       JOB_DBR jr;
183       memset(&jr, 0, sizeof(jr));
184       jr.JobId = bsr->JobId;
185       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
186          ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
187          return false;
188       }
189       bsr->VolSessionId = jr.VolSessionId;
190       bsr->VolSessionTime = jr.VolSessionTime;
191       if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
192            &(bsr->VolParams))) == 0) {
193          ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
194          if (bsr->VolParams) {
195             free(bsr->VolParams);
196             bsr->VolParams = NULL;
197          }
198          return false;
199       }
200    }
201    return true;
202 }
203
204 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
205 static uint32_t uniq = 0;
206
207 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
208 {
209    JCR *jcr = ua->jcr;
210    int i = find_arg_with_value(ua, "bootstrap");
211    if (i >= 0) {
212       Mmsg(fname, "%s", ua->argv[i]);              
213       jcr->unlink_bsr = false;
214    } else {
215       P(mutex);
216       uniq++;
217       V(mutex);
218       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
219       jcr->unlink_bsr = true;
220    }
221    if (jcr->RestoreBootstrap) {
222       free(jcr->RestoreBootstrap);
223    }
224    jcr->RestoreBootstrap = bstrdup(fname.c_str());
225 }
226
227 /*
228  * Write the bootstrap records to file
229  */
230 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
231 {
232    FILE *fd;
233    POOL_MEM fname(PM_MESSAGE);
234    uint32_t count = 0;;
235    bool err;
236
237    make_unique_restore_filename(ua, fname);
238    fd = fopen(fname.c_str(), "w+b");
239    if (!fd) {
240       berrno be;
241       ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
242          fname.c_str(), be.bstrerror());
243       goto bail_out;
244    }
245    /* Write them to file */
246    count = write_bsr(ua, rx, fd);
247    err = ferror(fd);
248    fclose(fd);
249    if (count == 0) {
250       ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
251       goto bail_out;
252    }
253    if (err) {
254       ua->error_msg(_("Error writing bsr file.\n"));
255       count = 0;
256       goto bail_out;
257    }
258
259    ua->send_msg(_("Bootstrap records written to %s\n"), fname.c_str());
260
261 bail_out:
262    return count;
263 }
264
265 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
266 {
267    char *p;
268    POOL_MEM volmsg(PM_MESSAGE);
269    JobId_t JobId;
270    char Device[MAX_NAME_LENGTH];
271    RBSR *bsr;
272
273    /* Tell the user what he will need to mount */
274    ua->send_msg("\n");
275    ua->send_msg(_("The job will require the following\n"
276                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
277                   "===========================================================================\n"));
278    /* Create Unique list of Volumes using prompt list */
279    start_prompt(ua, "");
280    if (*rx.JobIds == 0) {
281       /* Print Volumes in any order */
282       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
283          for (int i=0; i < bsr->VolCount; i++) {
284             if (bsr->VolParams[i].VolumeName[0]) {
285                if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
286                   Device[0] = 0;
287                }
288                Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
289                     bsr->VolParams[i].VolumeName, 
290                     bsr->VolParams[i].Storage, Device);
291                add_prompt(ua, volmsg.c_str());
292             }
293          }
294       }
295    } else {
296       /* Ensure that the volumes are printed in JobId order */
297       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
298          for (bsr=rx.bsr; bsr; bsr=bsr->next) {
299             if (JobId != bsr->JobId) {
300                continue;
301             }
302             for (int i=0; i < bsr->VolCount; i++) {
303                if (bsr->VolParams[i].VolumeName[0]) {
304                   if (!get_storage_device(Device, bsr->VolParams[i].Storage)) {
305                      Device[0] = 0;
306                   }
307                   Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
308                        bsr->VolParams[i].VolumeName, 
309                        bsr->VolParams[i].Storage, Device);
310                   add_prompt(ua, volmsg.c_str());
311                }
312             }
313          }
314       }
315    }
316    for (int i=0; i < ua->num_prompts; i++) {
317       ua->send_msg("   %s\n", ua->prompt[i]);
318       free(ua->prompt[i]);
319    }
320    if (ua->num_prompts == 0) {
321       ua->send_msg(_("No Volumes found to restore.\n"));
322    }
323    ua->num_prompts = 0;
324    ua->send_msg("\n");
325
326    return;
327 }
328
329 /*
330  * Here we actually write out the details of the bsr file.
331  *  Note, there is one bsr for each JobId, but the bsr may
332  *  have multiple volumes, which have been entered in the
333  *  order they were written.  
334  * The bsrs must be written out in the order the JobIds
335  *  are found in the jobid list.
336  */
337 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
338 {
339    uint32_t count = 0;
340    uint32_t total_count = 0;
341    uint32_t LastIndex = 0;
342    bool first = true;
343    char *p;
344    JobId_t JobId;
345    char device[MAX_NAME_LENGTH];
346    RBSR *bsr;
347    if (*rx.JobIds == 0) {
348       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
349          /*
350           * For a given volume, loop over all the JobMedia records.
351           *   VolCount is the number of JobMedia records.
352           */
353          for (int i=0; i < bsr->VolCount; i++) {
354             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
355                  bsr->VolParams[i].LastIndex)) {
356                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
357                continue;
358             }
359             if (!rx.store) {
360                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
361                                              bsr->VolParams[i].MediaType);
362             }
363             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
364             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
365             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
366                fprintf(fd, "Device=\"%s\"\n", device);
367             }
368             if (bsr->VolParams[i].Slot > 0) {
369                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
370             }
371             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
372             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
373             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
374                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
375             } else {
376                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
377                        bsr->VolParams[i].EndFile);
378             }
379             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
380                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
381             } else {
382                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
383                        bsr->VolParams[i].EndBlock);
384             }
385    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
386    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
387
388             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
389                                  bsr->VolParams[i].LastIndex, fd);
390             if (count) {
391                fprintf(fd, "Count=%u\n", count);
392             }
393             total_count += count;
394             /* If the same file is present on two tapes or in two files
395              *   on a tape, it is a continuation, and should not be treated
396              *   twice in the totals.
397              */
398             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
399                total_count--;
400             }
401             first = false;
402             LastIndex = bsr->VolParams[i].LastIndex;
403          }
404       }
405       return total_count;
406    }
407    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
408       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
409          if (JobId != bsr->JobId) {
410             continue;
411          }
412          /*
413           * For a given volume, loop over all the JobMedia records.
414           *   VolCount is the number of JobMedia records.
415           */
416          for (int i=0; i < bsr->VolCount; i++) {
417             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
418                  bsr->VolParams[i].LastIndex)) {
419                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
420                continue;
421             }
422             if (!rx.store) {
423                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
424                                              bsr->VolParams[i].MediaType);
425             }
426             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
427             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
428             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
429                fprintf(fd, "Device=\"%s\"\n", device);
430             }
431             if (bsr->VolParams[i].Slot > 0) {
432                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
433             }
434             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
435             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
436             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
437                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
438             } else {
439                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
440                        bsr->VolParams[i].EndFile);
441             }
442             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
443                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
444             } else {
445                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
446                        bsr->VolParams[i].EndBlock);
447             }
448    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
449    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
450
451             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
452                                  bsr->VolParams[i].LastIndex, fd);
453             if (count) {
454                fprintf(fd, "Count=%u\n", count);
455             }
456             total_count += count;
457             /* If the same file is present on two tapes or in two files
458              *   on a tape, it is a continuation, and should not be treated
459              *   twice in the totals.
460              */
461             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
462                total_count--;
463             }
464             first = false;
465             LastIndex = bsr->VolParams[i].LastIndex;
466          }
467       }
468    }
469    return total_count;
470 }
471
472 void print_bsr(UAContext *ua, RBSR *bsr)
473 {
474    for ( ; bsr; bsr=bsr->next) {
475       for (int i=0; i < bsr->VolCount; i++) {
476          ua->send_msg("Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
477          ua->send_msg("MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
478          ua->send_msg("VolSessionId=%u\n", bsr->VolSessionId);
479          ua->send_msg("VolSessionTime=%u\n", bsr->VolSessionTime);
480          ua->send_msg("VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
481                   bsr->VolParams[i].EndFile);
482          ua->send_msg("VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
483                   bsr->VolParams[i].EndBlock);
484          print_findex(ua, bsr->fi);
485       }
486       print_bsr(ua, bsr->next);
487    }
488 }
489
490
491
492
493 /*
494  * Add a FileIndex to the list of BootStrap records.
495  *  Here we are only dealing with JobId's and the FileIndexes
496  *  associated with those JobIds.
497  * We expect that JobId, FileIndex are sorted ascending.
498  */
499 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
500 {
501    RBSR *nbsr;
502    RBSR_FINDEX *fi, *lfi;
503
504    if (findex == 0) {
505       return;                         /* probably a dummy directory */
506    }
507
508    if (bsr->fi == NULL) {             /* if no FI add one */
509       /* This is the first FileIndex item in the chain */
510       bsr->fi = new_findex();
511       bsr->JobId = JobId;
512       bsr->fi->findex = findex;
513       bsr->fi->findex2 = findex;
514       return;
515    }
516    /* Walk down list of bsrs until we find the JobId */
517    if (bsr->JobId != JobId) {
518       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
519          if (nbsr->JobId == JobId) {
520             bsr = nbsr;
521             break;
522          }
523       }
524
525       if (!nbsr) {                    /* Must add new JobId */
526          /* Add new JobId at end of chain */
527          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
528             {  }
529          nbsr->next = new_bsr();
530          nbsr->next->JobId = JobId;
531          nbsr->next->fi = new_findex();
532          nbsr->next->fi->findex = findex;
533          nbsr->next->fi->findex2 = findex;
534          return;
535       }
536    }
537
538    /*
539     * At this point, bsr points to bsr containing this JobId,
540     *  and we are sure that there is at least one fi record.
541     */
542    lfi = fi = bsr->fi;
543    /* Check if this findex is smaller than first item */
544    if (findex < fi->findex) {
545       if ((findex+1) == fi->findex) {
546          fi->findex = findex;         /* extend down */
547          return;
548       }
549       fi = new_findex();              /* yes, insert before first item */
550       fi->findex = findex;
551       fi->findex2 = findex;
552       fi->next = lfi;
553       bsr->fi = fi;
554       return;
555    }
556    /* Walk down fi chain and find where to insert insert new FileIndex */
557    for ( ; fi; fi=fi->next) {
558       if (findex == (fi->findex2 + 1)) {  /* extend up */
559          RBSR_FINDEX *nfi;
560          fi->findex2 = findex;
561          /*
562           * If the following record contains one higher, merge its
563           *   file index by extending it up.
564           */
565          if (fi->next && ((findex+1) == fi->next->findex)) {
566             nfi = fi->next;
567             fi->findex2 = nfi->findex2;
568             fi->next = nfi->next;
569             free(nfi);
570          }
571          return;
572       }
573       if (findex < fi->findex) {      /* add before */
574          if ((findex+1) == fi->findex) {
575             fi->findex = findex;
576             return;
577          }
578          break;
579       }
580       lfi = fi;
581    }
582    /* Add to last place found */
583    fi = new_findex();
584    fi->findex = findex;
585    fi->findex2 = findex;
586    fi->next = lfi->next;
587    lfi->next = fi;
588    return;
589 }
590
591 /*
592  * Add all possible  FileIndexes to the list of BootStrap records.
593  *  Here we are only dealing with JobId's and the FileIndexes
594  *  associated with those JobIds.
595  */
596 void add_findex_all(RBSR *bsr, uint32_t JobId)
597 {
598    RBSR *nbsr;
599    RBSR_FINDEX *fi;
600
601    if (bsr->fi == NULL) {             /* if no FI add one */
602       /* This is the first FileIndex item in the chain */
603       bsr->fi = new_findex();
604       bsr->JobId = JobId;
605       bsr->fi->findex = 1;
606       bsr->fi->findex2 = INT32_MAX;
607       return;
608    }
609    /* Walk down list of bsrs until we find the JobId */
610    if (bsr->JobId != JobId) {
611       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
612          if (nbsr->JobId == JobId) {
613             bsr = nbsr;
614             break;
615          }
616       }
617
618       if (!nbsr) {                    /* Must add new JobId */
619          /* Add new JobId at end of chain */
620          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
621             {  }
622          nbsr->next = new_bsr();
623          nbsr->next->JobId = JobId;
624          nbsr->next->fi = new_findex();
625          nbsr->next->fi->findex = 1;
626          nbsr->next->fi->findex2 = INT32_MAX;
627          return;
628       }
629    }
630
631    /*
632     * At this point, bsr points to bsr containing this JobId,
633     *  and we are sure that there is at least one fi record.
634     */
635    fi = bsr->fi;
636    fi->findex = 1;
637    fi->findex2 = INT32_MAX;
638    return;
639 }