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