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