]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
Apply patch submitted for bug #1107 with a small modification.
[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
272    /* Tell the user what he will need to mount */
273    ua->send_msg("\n");
274    ua->send_msg(_("The job will require the following\n"
275                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
276                   "===========================================================================\n"));
277    /* Create Unique list of Volumes using prompt list */
278    start_prompt(ua, "");
279    if (*rx.JobIds) {
280       /* Ensure that the volumes are printed in JobId order */
281       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
282          for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
283             if (JobId != nbsr->JobId) {
284                continue;
285             }
286             for (int i=0; i < nbsr->VolCount; i++) {
287                if (nbsr->VolParams[i].VolumeName[0]) {
288                   if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
289                      Device[0] = 0;
290                   }
291                   Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
292                        nbsr->VolParams[i].VolumeName, 
293                        nbsr->VolParams[i].Storage, Device);
294                   add_prompt(ua, volmsg.c_str());
295                }
296             }
297          }
298       }
299    } else {
300       /* Print Volumes in any order */
301       for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
302          for (int i=0; i < nbsr->VolCount; i++) {
303             if (nbsr->VolParams[i].VolumeName[0]) {
304                if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
305                   Device[0] = 0;
306                }
307                Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
308                     nbsr->VolParams[i].VolumeName, 
309                     nbsr->VolParams[i].Storage, Device);
310                add_prompt(ua, volmsg.c_str());
311             }
312          }
313       }
314    }
315    for (int i=0; i < ua->num_prompts; i++) {
316       ua->send_msg("   %s\n", ua->prompt[i]);
317       free(ua->prompt[i]);
318    }
319    if (ua->num_prompts == 0) {
320       ua->send_msg(_("No Volumes found to restore.\n"));
321    }
322    ua->num_prompts = 0;
323    ua->send_msg("\n");
324
325    return;
326 }
327
328 /*
329  * Here we actually write out the details of the bsr file.
330  *  Note, there is one bsr for each JobId, but the bsr may
331  *  have multiple volumes, which have been entered in the
332  *  order they were written.  
333  * The bsrs must be written out in the order the JobIds
334  *  are found in the jobid list.
335  */
336 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
337 {
338    uint32_t count = 0;
339    uint32_t total_count = 0;
340    uint32_t LastIndex = 0;
341    bool first = true;
342    char *p;
343    JobId_t JobId;
344    char device[MAX_NAME_LENGTH];
345    RBSR *bsr;
346    if (*rx.JobIds == 0) {
347       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
348          /*
349           * For a given volume, loop over all the JobMedia records.
350           *   VolCount is the number of JobMedia records.
351           */
352          for (int i=0; i < bsr->VolCount; i++) {
353             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
354                  bsr->VolParams[i].LastIndex)) {
355                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
356                continue;
357             }
358             if (!rx.store) {
359                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
360                                              bsr->VolParams[i].MediaType);
361             }
362             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
363             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
364             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
365                fprintf(fd, "Device=\"%s\"\n", device);
366             }
367             if (bsr->VolParams[i].Slot > 0) {
368                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
369             }
370             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
371             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
372             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
373                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
374             } else {
375                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
376                        bsr->VolParams[i].EndFile);
377             }
378             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
379                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
380             } else {
381                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
382                        bsr->VolParams[i].EndBlock);
383             }
384    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
385    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
386
387             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
388                                  bsr->VolParams[i].LastIndex, fd);
389             if (count) {
390                fprintf(fd, "Count=%u\n", count);
391             }
392             total_count += count;
393             /* If the same file is present on two tapes or in two files
394              *   on a tape, it is a continuation, and should not be treated
395              *   twice in the totals.
396              */
397             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
398                total_count--;
399             }
400             first = false;
401             LastIndex = bsr->VolParams[i].LastIndex;
402          }
403       }
404       return total_count;
405    }
406    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
407       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
408          if (JobId != bsr->JobId) {
409             continue;
410          }
411          /*
412           * For a given volume, loop over all the JobMedia records.
413           *   VolCount is the number of JobMedia records.
414           */
415          for (int i=0; i < bsr->VolCount; i++) {
416             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
417                  bsr->VolParams[i].LastIndex)) {
418                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
419                continue;
420             }
421             if (!rx.store) {
422                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
423                                              bsr->VolParams[i].MediaType);
424             }
425             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
426             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
427             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
428                fprintf(fd, "Device=\"%s\"\n", device);
429             }
430             if (bsr->VolParams[i].Slot > 0) {
431                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
432             }
433             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
434             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
435             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
436                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
437             } else {
438                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
439                        bsr->VolParams[i].EndFile);
440             }
441             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
442                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
443             } else {
444                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
445                        bsr->VolParams[i].EndBlock);
446             }
447    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
448    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
449
450             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
451                                  bsr->VolParams[i].LastIndex, fd);
452             if (count) {
453                fprintf(fd, "Count=%u\n", count);
454             }
455             total_count += count;
456             /* If the same file is present on two tapes or in two files
457              *   on a tape, it is a continuation, and should not be treated
458              *   twice in the totals.
459              */
460             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
461                total_count--;
462             }
463             first = false;
464             LastIndex = bsr->VolParams[i].LastIndex;
465          }
466       }
467    }
468    return total_count;
469 }
470
471 void print_bsr(UAContext *ua, RBSR *bsr)
472 {
473    for ( ; bsr; bsr=bsr->next) {
474       for (int i=0; i < bsr->VolCount; i++) {
475          ua->send_msg("Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
476          ua->send_msg("MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
477          ua->send_msg("VolSessionId=%u\n", bsr->VolSessionId);
478          ua->send_msg("VolSessionTime=%u\n", bsr->VolSessionTime);
479          ua->send_msg("VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
480                   bsr->VolParams[i].EndFile);
481          ua->send_msg("VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
482                   bsr->VolParams[i].EndBlock);
483          print_findex(ua, bsr->fi);
484       }
485       print_bsr(ua, bsr->next);
486    }
487 }
488
489
490
491
492 /*
493  * Add a FileIndex to the list of BootStrap records.
494  *  Here we are only dealing with JobId's and the FileIndexes
495  *  associated with those JobIds.
496  * We expect that JobId, FileIndex are sorted ascending.
497  */
498 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
499 {
500    RBSR *nbsr;
501    RBSR_FINDEX *fi, *lfi;
502
503    if (findex == 0) {
504       return;                         /* probably a dummy directory */
505    }
506
507    if (bsr->fi == NULL) {             /* if no FI add one */
508       /* This is the first FileIndex item in the chain */
509       bsr->fi = new_findex();
510       bsr->JobId = JobId;
511       bsr->fi->findex = findex;
512       bsr->fi->findex2 = findex;
513       return;
514    }
515    /* Walk down list of bsrs until we find the JobId */
516    if (bsr->JobId != JobId) {
517       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
518          if (nbsr->JobId == JobId) {
519             bsr = nbsr;
520             break;
521          }
522       }
523
524       if (!nbsr) {                    /* Must add new JobId */
525          /* Add new JobId at end of chain */
526          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
527             {  }
528          nbsr->next = new_bsr();
529          nbsr->next->JobId = JobId;
530          nbsr->next->fi = new_findex();
531          nbsr->next->fi->findex = findex;
532          nbsr->next->fi->findex2 = findex;
533          return;
534       }
535    }
536
537    /*
538     * At this point, bsr points to bsr containing this JobId,
539     *  and we are sure that there is at least one fi record.
540     */
541    lfi = fi = bsr->fi;
542    /* Check if this findex is smaller than first item */
543    if (findex < fi->findex) {
544       if ((findex+1) == fi->findex) {
545          fi->findex = findex;         /* extend down */
546          return;
547       }
548       fi = new_findex();              /* yes, insert before first item */
549       fi->findex = findex;
550       fi->findex2 = findex;
551       fi->next = lfi;
552       bsr->fi = fi;
553       return;
554    }
555    /* Walk down fi chain and find where to insert insert new FileIndex */
556    for ( ; fi; fi=fi->next) {
557       if (findex == (fi->findex2 + 1)) {  /* extend up */
558          RBSR_FINDEX *nfi;
559          fi->findex2 = findex;
560          /*
561           * If the following record contains one higher, merge its
562           *   file index by extending it up.
563           */
564          if (fi->next && ((findex+1) == fi->next->findex)) {
565             nfi = fi->next;
566             fi->findex2 = nfi->findex2;
567             fi->next = nfi->next;
568             free(nfi);
569          }
570          return;
571       }
572       if (findex < fi->findex) {      /* add before */
573          if ((findex+1) == fi->findex) {
574             fi->findex = findex;
575             return;
576          }
577          break;
578       }
579       lfi = fi;
580    }
581    /* Add to last place found */
582    fi = new_findex();
583    fi->findex = findex;
584    fi->findex2 = findex;
585    fi->next = lfi->next;
586    lfi->next = fi;
587    return;
588 }
589
590 /*
591  * Add all possible  FileIndexes to the list of BootStrap records.
592  *  Here we are only dealing with JobId's and the FileIndexes
593  *  associated with those JobIds.
594  */
595 void add_findex_all(RBSR *bsr, uint32_t JobId)
596 {
597    RBSR *nbsr;
598    RBSR_FINDEX *fi;
599
600    if (bsr->fi == NULL) {             /* if no FI add one */
601       /* This is the first FileIndex item in the chain */
602       bsr->fi = new_findex();
603       bsr->JobId = JobId;
604       bsr->fi->findex = 1;
605       bsr->fi->findex2 = INT32_MAX;
606       return;
607    }
608    /* Walk down list of bsrs until we find the JobId */
609    if (bsr->JobId != JobId) {
610       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
611          if (nbsr->JobId == JobId) {
612             bsr = nbsr;
613             break;
614          }
615       }
616
617       if (!nbsr) {                    /* Must add new JobId */
618          /* Add new JobId at end of chain */
619          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
620             {  }
621          nbsr->next = new_bsr();
622          nbsr->next->JobId = JobId;
623          nbsr->next->fi = new_findex();
624          nbsr->next->fi->findex = 1;
625          nbsr->next->fi->findex2 = INT32_MAX;
626          return;
627       }
628    }
629
630    /*
631     * At this point, bsr points to bsr containing this JobId,
632     *  and we are sure that there is at least one fi record.
633     */
634    fi = bsr->fi;
635    fi->findex = 1;
636    fi->findex2 = INT32_MAX;
637    return;
638 }