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