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