]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
Switch from GPLv2 to AGPLv3
[bacula/bacula] / bacula / src / dird / bsr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2010 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 three of the GNU Affero 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 Affero 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  */
38
39 #include "bacula.h"
40 #include "dird.h"
41
42 /* Forward referenced functions */
43 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd);
44
45 /*
46  * Create new FileIndex entry for BSR
47  */
48 RBSR_FINDEX *new_findex()
49 {
50    RBSR_FINDEX *fi = (RBSR_FINDEX *)bmalloc(sizeof(RBSR_FINDEX));
51    memset(fi, 0, sizeof(RBSR_FINDEX));
52    return fi;
53 }
54
55 /* Free all BSR FileIndex entries */
56 static void free_findex(RBSR_FINDEX *fi)
57 {
58    RBSR_FINDEX *next;
59    for ( ; fi; fi=next) {
60       next = fi->next;
61       free(fi);
62    }
63 }
64
65 /* 
66  * Get storage device name from Storage resource
67  */
68 static bool get_storage_device(char *device, char *storage)
69 {
70    STORE *store;
71    if (storage[0] == 0) {
72       return false;
73    }
74    store = (STORE *)GetResWithName(R_STORAGE, storage);    
75    if (!store) {
76       return false;
77    }
78    DEVICE *dev = (DEVICE *)(store->device->first());
79    if (!dev) {
80       return false;
81    }
82    bstrncpy(device, dev->hdr.name, MAX_NAME_LENGTH);
83    return true;
84 }
85
86 /*
87  * Our data structures were not designed completely
88  *  correctly, so the file indexes cover the full
89  *  range regardless of volume. The FirstIndex and LastIndex
90  *  passed in here are for the current volume, so when
91  *  writing out the fi, constrain them to those values.
92  *
93  * We are called here once for each JobMedia record
94  *  for each Volume.
95  */
96 static uint32_t write_findex(RBSR_FINDEX *fi,
97               int32_t FirstIndex, int32_t LastIndex, FILE *fd)
98 {
99    uint32_t count = 0;
100    for ( ; fi; fi=fi->next) {
101       int32_t findex, findex2;
102       if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
103           (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
104           (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
105          findex = fi->findex < FirstIndex ? FirstIndex : fi->findex;
106          findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2;
107          if (findex == findex2) {
108             fprintf(fd, "FileIndex=%d\n", findex);
109             count++;
110          } else {
111             fprintf(fd, "FileIndex=%d-%d\n", findex, findex2);
112             count += findex2 - findex + 1;
113          }
114       }
115    }
116    return count;
117 }
118
119 /*
120  * Find out if Volume defined with FirstIndex and LastIndex
121  *   falls within the range of selected files in the bsr.
122  */
123 static bool is_volume_selected(RBSR_FINDEX *fi,
124               int32_t FirstIndex, int32_t LastIndex)
125 {
126    for ( ; fi; fi=fi->next) {
127       if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) ||
128           (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) ||
129           (fi->findex < FirstIndex && fi->findex2 > LastIndex)) {
130          return true;
131       }
132    }
133    return false;
134 }
135
136
137 /* Create a new bootstrap record */
138 RBSR *new_bsr()
139 {
140    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
141    memset(bsr, 0, sizeof(RBSR));
142    return bsr;
143 }
144
145 /* Free the entire BSR */
146 void free_bsr(RBSR *bsr)
147 {
148    RBSR *next;
149    for ( ; bsr; bsr=next) {
150       free_findex(bsr->fi);
151       if (bsr->VolParams) {
152          free(bsr->VolParams);
153       }
154       if (bsr->fileregex) {
155          free(bsr->fileregex);
156       }
157       next = bsr->next;
158       free(bsr);
159    }
160 }
161
162 /*
163  * Complete the BSR by filling in the VolumeName and
164  *  VolSessionId and VolSessionTime using the JobId
165  */
166 bool complete_bsr(UAContext *ua, RBSR *bsr)
167 {
168    for ( ; bsr; bsr=bsr->next) {
169       JOB_DBR jr;
170       memset(&jr, 0, sizeof(jr));
171       jr.JobId = bsr->JobId;
172       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
173          ua->error_msg(_("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
174          return false;
175       }
176       bsr->VolSessionId = jr.VolSessionId;
177       bsr->VolSessionTime = jr.VolSessionTime;
178       if (jr.JobFiles == 0) {      /* zero files is OK, not an error, but */
179          bsr->VolCount = 0;        /*   there are no volumes */
180          continue;
181       }
182       if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
183            &(bsr->VolParams))) == 0) {
184          ua->error_msg(_("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
185          if (bsr->VolParams) {
186             free(bsr->VolParams);
187             bsr->VolParams = NULL;
188          }
189          return false;
190       }
191    }
192    return true;
193 }
194
195 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
196 static uint32_t uniq = 0;
197
198 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
199 {
200    JCR *jcr = ua->jcr;
201    int i = find_arg_with_value(ua, "bootstrap");
202    if (i >= 0) {
203       Mmsg(fname, "%s", ua->argv[i]);              
204       jcr->unlink_bsr = false;
205    } else {
206       P(mutex);
207       uniq++;
208       V(mutex);
209       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
210       jcr->unlink_bsr = true;
211    }
212    if (jcr->RestoreBootstrap) {
213       free(jcr->RestoreBootstrap);
214    }
215    jcr->RestoreBootstrap = bstrdup(fname.c_str());
216 }
217
218 /*
219  * Write the bootstrap records to file
220  */
221 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
222 {
223    FILE *fd;
224    POOL_MEM fname(PM_MESSAGE);
225    uint32_t count = 0;;
226    bool err;
227
228    make_unique_restore_filename(ua, fname);
229    fd = fopen(fname.c_str(), "w+b");
230    if (!fd) {
231       berrno be;
232       ua->error_msg(_("Unable to create bootstrap file %s. ERR=%s\n"),
233          fname.c_str(), be.bstrerror());
234       goto bail_out;
235    }
236    /* Write them to file */
237    count = write_bsr(ua, rx, fd);
238    err = ferror(fd);
239    fclose(fd);
240    if (count == 0) {
241       ua->info_msg(_("No files found to read. No bootstrap file written.\n"));
242       goto bail_out;
243    }
244    if (err) {
245       ua->error_msg(_("Error writing bsr file.\n"));
246       count = 0;
247       goto bail_out;
248    }
249
250    ua->send_msg(_("Bootstrap records written to %s\n"), fname.c_str());
251
252    if (debug_level >= 10) {
253       print_bsr(ua, rx);
254    }
255
256 bail_out:
257    return count;
258 }
259
260 static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId)
261 {
262    POOL_MEM volmsg(PM_MESSAGE);
263    char Device[MAX_NAME_LENGTH];
264    char online;
265    RBSR *bsr;
266
267    for (bsr=rx.bsr; bsr; bsr=bsr->next) {
268       if (JobId && JobId != bsr->JobId) {
269          continue;
270       }
271
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             if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) {
278                online = '*';
279             } else {
280                online = ' ';
281             }
282             Mmsg(volmsg, "%c%-25s %-25s %-25s", 
283                  online, bsr->VolParams[i].VolumeName,
284                  bsr->VolParams[i].Storage, Device);
285             add_prompt(ua, volmsg.c_str());
286          }
287       }
288    }
289 }
290
291 void display_bsr_info(UAContext *ua, RESTORE_CTX &rx)
292 {
293    char *p;
294    JobId_t JobId;
295
296    /* Tell the user what he will need to mount */
297    ua->send_msg("\n");
298    ua->send_msg(_("The job will require the following\n"
299                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
300                   "===========================================================================\n"));
301    /* Create Unique list of Volumes using prompt list */
302    start_prompt(ua, "");
303    if (*rx.JobIds == 0) {
304       /* Print Volumes in any order */
305       display_vol_info(ua, rx, 0);
306    } else {
307       /* Ensure that the volumes are printed in JobId order */
308       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
309          display_vol_info(ua, rx, JobId);
310       }
311    }
312    for (int i=0; i < ua->num_prompts; i++) {
313       ua->send_msg("   %s\n", ua->prompt[i]);
314       free(ua->prompt[i]);
315    }
316    if (ua->num_prompts == 0) {
317       ua->send_msg(_("No Volumes found to restore.\n"));
318    } else {
319       ua->send_msg(_("\nVolumes marked with \"*\" are online.\n"));
320    }
321    ua->num_prompts = 0;
322    ua->send_msg("\n");
323
324    return;
325 }
326
327 /*
328  * Write bsr data for a single bsr record
329  */
330 static uint32_t write_bsr_item(RBSR *bsr, UAContext *ua, 
331                    RESTORE_CTX &rx, FILE *fd, bool &first, uint32_t &LastIndex)
332 {
333    char ed1[50], ed2[50];
334    uint32_t count = 0;
335    uint32_t total_count = 0;
336    char device[MAX_NAME_LENGTH];
337
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, "Storage=\"%s\"\n", bsr->VolParams[i].Storage);
353       fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
354       fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
355       if (bsr->fileregex) {
356          fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
357       }
358       if (get_storage_device(device, bsr->VolParams[i].Storage)) {
359          fprintf(fd, "Device=\"%s\"\n", device);
360       }
361       if (bsr->VolParams[i].Slot > 0) {
362          fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
363       }
364       fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
365       fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
366       fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
367               edit_uint64(bsr->VolParams[i].EndAddr, ed2));
368 //    Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
369 //      bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
370
371       count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
372                            bsr->VolParams[i].LastIndex, fd);
373       if (count) {
374          fprintf(fd, "Count=%u\n", count);
375       }
376       total_count += count;
377       /* If the same file is present on two tapes or in two files
378        *   on a tape, it is a continuation, and should not be treated
379        *   twice in the totals.
380        */
381       if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
382          total_count--;
383       }
384       first = false;
385       LastIndex = bsr->VolParams[i].LastIndex;
386    }
387    return total_count;
388 }
389
390
391 /*
392  * Here we actually write out the details of the bsr file.
393  *  Note, there is one bsr for each JobId, but the bsr may
394  *  have multiple volumes, which have been entered in the
395  *  order they were written.  
396  * The bsrs must be written out in the order the JobIds
397  *  are found in the jobid list.
398  */
399 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
400 {
401    bool first = true;
402    uint32_t LastIndex = 0;
403    uint32_t total_count = 0;
404    char *p;
405    JobId_t JobId;
406    RBSR *bsr;
407    if (*rx.JobIds == 0) {
408       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
409          total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
410       }
411       return total_count;
412    }
413    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
414       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
415          if (JobId == bsr->JobId) {
416             total_count += write_bsr_item(bsr, ua, rx, fd, first, LastIndex);
417          }
418       }
419    }
420    return total_count;
421 }
422
423 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
424 {
425    write_bsr(ua, rx, stdout);
426 }
427
428
429
430
431 /*
432  * Add a FileIndex to the list of BootStrap records.
433  *  Here we are only dealing with JobId's and the FileIndexes
434  *  associated with those JobIds.
435  * We expect that JobId, FileIndex are sorted ascending.
436  */
437 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
438 {
439    RBSR *nbsr;
440    RBSR_FINDEX *fi, *lfi;
441
442    if (findex == 0) {
443       return;                         /* probably a dummy directory */
444    }
445
446    if (bsr->fi == NULL) {             /* if no FI add one */
447       /* This is the first FileIndex item in the chain */
448       bsr->fi = new_findex();
449       bsr->JobId = JobId;
450       bsr->fi->findex = findex;
451       bsr->fi->findex2 = findex;
452       return;
453    }
454    /* Walk down list of bsrs until we find the JobId */
455    if (bsr->JobId != JobId) {
456       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
457          if (nbsr->JobId == JobId) {
458             bsr = nbsr;
459             break;
460          }
461       }
462
463       if (!nbsr) {                    /* Must add new JobId */
464          /* Add new JobId at end of chain */
465          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
466             {  }
467          nbsr->next = new_bsr();
468          nbsr->next->JobId = JobId;
469          nbsr->next->fi = new_findex();
470          nbsr->next->fi->findex = findex;
471          nbsr->next->fi->findex2 = findex;
472          return;
473       }
474    }
475
476    /*
477     * At this point, bsr points to bsr containing this JobId,
478     *  and we are sure that there is at least one fi record.
479     */
480    lfi = fi = bsr->fi;
481    /* Check if this findex is smaller than first item */
482    if (findex < fi->findex) {
483       if ((findex+1) == fi->findex) {
484          fi->findex = findex;         /* extend down */
485          return;
486       }
487       fi = new_findex();              /* yes, insert before first item */
488       fi->findex = findex;
489       fi->findex2 = findex;
490       fi->next = lfi;
491       bsr->fi = fi;
492       return;
493    }
494    /* Walk down fi chain and find where to insert insert new FileIndex */
495    for ( ; fi; fi=fi->next) {
496       if (findex == (fi->findex2 + 1)) {  /* extend up */
497          RBSR_FINDEX *nfi;
498          fi->findex2 = findex;
499          /*
500           * If the following record contains one higher, merge its
501           *   file index by extending it up.
502           */
503          if (fi->next && ((findex+1) == fi->next->findex)) {
504             nfi = fi->next;
505             fi->findex2 = nfi->findex2;
506             fi->next = nfi->next;
507             free(nfi);
508          }
509          return;
510       }
511       if (findex < fi->findex) {      /* add before */
512          if ((findex+1) == fi->findex) {
513             fi->findex = findex;
514             return;
515          }
516          break;
517       }
518       lfi = fi;
519    }
520    /* Add to last place found */
521    fi = new_findex();
522    fi->findex = findex;
523    fi->findex2 = findex;
524    fi->next = lfi->next;
525    lfi->next = fi;
526    return;
527 }
528
529 /*
530  * Add all possible  FileIndexes to the list of BootStrap records.
531  *  Here we are only dealing with JobId's and the FileIndexes
532  *  associated with those JobIds.
533  */
534 void add_findex_all(RBSR *bsr, uint32_t JobId)
535 {
536    RBSR *nbsr;
537    RBSR_FINDEX *fi;
538
539    if (bsr->fi == NULL) {             /* if no FI add one */
540       /* This is the first FileIndex item in the chain */
541       bsr->fi = new_findex();
542       bsr->JobId = JobId;
543       bsr->fi->findex = 1;
544       bsr->fi->findex2 = INT32_MAX;
545       return;
546    }
547    /* Walk down list of bsrs until we find the JobId */
548    if (bsr->JobId != JobId) {
549       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
550          if (nbsr->JobId == JobId) {
551             bsr = nbsr;
552             break;
553          }
554       }
555
556       if (!nbsr) {                    /* Must add new JobId */
557          /* Add new JobId at end of chain */
558          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
559             {  }
560
561          nbsr->next = new_bsr();
562          nbsr->next->JobId = JobId;
563
564          /* If we use regexp to restore, set it for each jobid */
565          if (bsr->fileregex) { 
566             nbsr->next->fileregex = bstrdup(bsr->fileregex);
567          }
568
569          nbsr->next->fi = new_findex();
570          nbsr->next->fi->findex = 1;
571          nbsr->next->fi->findex2 = INT32_MAX;
572          return;
573       }
574    }
575
576    /*
577     * At this point, bsr points to bsr containing this JobId,
578     *  and we are sure that there is at least one fi record.
579     */
580    fi = bsr->fi;
581    fi->findex = 1;
582    fi->findex2 = INT32_MAX;
583    return;
584 }