]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bsr.c
kes Add back code to open tape device nonblocking, but if rewind fails
[bacula/bacula] / bacula / src / dird / bsr.c
1 /*
2  *
3  *   Bacula Director -- Bootstrap Record routines.
4  *
5  *      BSR (bootstrap record) handling routines split from
6  *        ua_restore.c July MMIII
7  *
8  *     Kern Sibbald, July MMII
9  *
10  *   Version $Id$
11  */
12
13 /*
14    Copyright (C) 2002-2006 Kern Sibbald
15
16    This program is free software; you can redistribute it and/or
17    modify it under the terms of the GNU General Public License
18    version 2 as amended with additional clauses defined in the
19    file LICENSE in the main source directory.
20
21    This program is distributed in the hope that it will be useful,
22    but WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
24    the file LICENSE for additional details.
25
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 void print_bsr(UAContext *ua, RBSR *bsr);
34
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
129 static void print_findex(UAContext *ua, RBSR_FINDEX *fi)
130 {
131    bsendmsg(ua, "fi=0x%lx\n", fi);
132    for ( ; fi; fi=fi->next) {
133       if (fi->findex == fi->findex2) {
134          bsendmsg(ua, "FileIndex=%d\n", fi->findex);
135          Dmsg1(1000, "FileIndex=%d\n", fi->findex);
136       } else {
137          bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
138          Dmsg2(1000, "FileIndex=%d-%d\n", fi->findex, fi->findex2);
139       }
140    }
141 }
142
143 /* Create a new bootstrap record */
144 RBSR *new_bsr()
145 {
146    RBSR *bsr = (RBSR *)bmalloc(sizeof(RBSR));
147    memset(bsr, 0, sizeof(RBSR));
148    return bsr;
149 }
150
151 /* Free the entire BSR */
152 void free_bsr(RBSR *bsr)
153 {
154    RBSR *next;
155    for ( ; bsr; bsr=next) {
156       free_findex(bsr->fi);
157       if (bsr->VolParams) {
158          free(bsr->VolParams);
159       }
160       next = bsr->next;
161       free(bsr);
162    }
163 }
164
165 /*
166  * Complete the BSR by filling in the VolumeName and
167  *  VolSessionId and VolSessionTime using the JobId
168  */
169 bool complete_bsr(UAContext *ua, RBSR *bsr)
170 {
171    for ( ; bsr; bsr=bsr->next) {
172       JOB_DBR jr;
173       memset(&jr, 0, sizeof(jr));
174       jr.JobId = bsr->JobId;
175       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
176          bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
177          return false;
178       }
179       bsr->VolSessionId = jr.VolSessionId;
180       bsr->VolSessionTime = jr.VolSessionTime;
181       if ((bsr->VolCount=db_get_job_volume_parameters(ua->jcr, ua->db, bsr->JobId,
182            &(bsr->VolParams))) == 0) {
183          bsendmsg(ua, _("Unable to get Job Volume Parameters. ERR=%s\n"), db_strerror(ua->db));
184          if (bsr->VolParams) {
185             free(bsr->VolParams);
186             bsr->VolParams = NULL;
187          }
188          return false;
189       }
190    }
191    return true;
192 }
193
194 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
195 static uint32_t uniq = 0;
196
197 static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname)
198 {
199    JCR *jcr = ua->jcr;
200    int i = find_arg_with_value(ua, "bootstrap");
201    if (i >= 0) {
202       Mmsg(fname, "%s", ua->argv[i]);              
203       jcr->unlink_bsr = false;
204    } else {
205       P(mutex);
206       uniq++;
207       V(mutex);
208       Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq);
209       jcr->unlink_bsr = true;
210    }
211    if (jcr->RestoreBootstrap) {
212       free(jcr->RestoreBootstrap);
213    }
214    jcr->RestoreBootstrap = bstrdup(fname.c_str());
215 }
216
217 /*
218  * Write the bootstrap records to file
219  */
220 uint32_t write_bsr_file(UAContext *ua, RESTORE_CTX &rx)
221 {
222    FILE *fd;
223    POOL_MEM fname(PM_MESSAGE);
224    POOL_MEM volmsg(PM_MESSAGE);
225    uint32_t count = 0;;
226    bool err;
227    char *p;
228    JobId_t JobId;
229    char Device[MAX_NAME_LENGTH];
230
231    make_unique_restore_filename(ua, fname);
232    fd = fopen(fname.c_str(), "w+b");
233    if (!fd) {
234       berrno be;
235       bsendmsg(ua, _("Unable to create bootstrap file %s. ERR=%s\n"),
236          fname.c_str(), be.strerror());
237       goto bail_out;
238    }
239    /* Write them to file */
240    count = write_bsr(ua, rx, fd);
241    err = ferror(fd);
242    fclose(fd);
243    if (count == 0) {
244       bsendmsg(ua, _("No files found to restore.\n"));
245       goto bail_out;
246    }
247    if (err) {
248       bsendmsg(ua, _("Error writing bsr file.\n"));
249       count = 0;
250       goto bail_out;
251    }
252
253
254    bsendmsg(ua, _("Bootstrap records written to %s\n"), fname.c_str());
255
256    /* Tell the user what he will need to mount */
257    bsendmsg(ua, "\n");
258    bsendmsg(ua, _("The job will require the following\n"
259                   "   Volume(s)                 Storage(s)                SD Device(s)\n"
260                   "===========================================================================\n"));
261    /* Create Unique list of Volumes using prompt list */
262    start_prompt(ua, "");
263    if (*rx.JobIds) {
264       /* Ensure that the volumes are printed in JobId order */
265       for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
266          for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
267             if (JobId != nbsr->JobId) {
268                continue;
269             }
270             for (int i=0; i < nbsr->VolCount; i++) {
271                if (nbsr->VolParams[i].VolumeName[0]) {
272                   if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
273                      Device[0] = 0;
274                   }
275                   Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
276                        nbsr->VolParams[i].VolumeName, 
277                        nbsr->VolParams[i].Storage, Device);
278                   add_prompt(ua, volmsg.c_str());
279                }
280             }
281          }
282       }
283    } else {
284       /* Print Volumes in any order */
285       for (RBSR *nbsr=rx.bsr; nbsr; nbsr=nbsr->next) {
286          for (int i=0; i < nbsr->VolCount; i++) {
287             if (nbsr->VolParams[i].VolumeName[0]) {
288                if (!get_storage_device(Device, nbsr->VolParams[i].Storage)) {
289                   Device[0] = 0;
290                }
291                Mmsg(volmsg, "%-25.25s %-25.25s %-25.25s", 
292                     nbsr->VolParams[i].VolumeName, 
293                     nbsr->VolParams[i].Storage, Device);
294                add_prompt(ua, volmsg.c_str());
295             }
296          }
297       }
298    }
299    for (int i=0; i < ua->num_prompts; i++) {
300       bsendmsg(ua, "   %s\n", ua->prompt[i]);
301       free(ua->prompt[i]);
302    }
303    if (ua->num_prompts == 0) {
304       bsendmsg(ua, _("No Volumes found to restore.\n"));
305       count = 0;
306    }
307    ua->num_prompts = 0;
308    bsendmsg(ua, "\n");
309
310 bail_out:
311    return count;
312 }
313
314 /*
315  * Here we actually write out the details of the bsr file.
316  *  Note, there is one bsr for each JobId, but the bsr may
317  *  have multiple volumes, which have been entered in the
318  *  order they were written.  
319  * The bsrs must be written out in the order the JobIds
320  *  are found in the jobid list.
321  */
322 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
323 {
324    uint32_t count = 0;
325    uint32_t total_count = 0;
326    uint32_t LastIndex = 0;
327    bool first = true;
328    char *p;
329    JobId_t JobId;
330    char device[MAX_NAME_LENGTH];
331    RBSR *bsr;
332    if (*rx.JobIds == 0) {
333       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
334          /*
335           * For a given volume, loop over all the JobMedia records.
336           *   VolCount is the number of JobMedia records.
337           */
338          for (int i=0; i < bsr->VolCount; i++) {
339             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
340                  bsr->VolParams[i].LastIndex)) {
341                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
342                continue;
343             }
344             if (!rx.store) {
345                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
346                                              bsr->VolParams[i].MediaType);
347             }
348             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
349             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
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             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
359                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
360             } else {
361                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
362                        bsr->VolParams[i].EndFile);
363             }
364             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
365                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
366             } else {
367                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
368                        bsr->VolParams[i].EndBlock);
369             }
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       }
390       return total_count;
391    }
392    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
393       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
394          if (JobId != bsr->JobId) {
395             continue;
396          }
397          /*
398           * For a given volume, loop over all the JobMedia records.
399           *   VolCount is the number of JobMedia records.
400           */
401          for (int i=0; i < bsr->VolCount; i++) {
402             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
403                  bsr->VolParams[i].LastIndex)) {
404                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
405                continue;
406             }
407             if (!rx.store) {
408                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
409                                              bsr->VolParams[i].MediaType);
410             }
411             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
412             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
413             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
414                fprintf(fd, "Device=\"%s\"\n", device);
415             }
416             if (bsr->VolParams[i].Slot > 0) {
417                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
418             }
419             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
420             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
421             if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) {
422                fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile);
423             } else {
424                fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
425                        bsr->VolParams[i].EndFile);
426             }
427             if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) {
428                fprintf(fd, "VolBlock=%u\n", bsr->VolParams[i].StartBlock);
429             } else {
430                fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
431                        bsr->VolParams[i].EndBlock);
432             }
433    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
434    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
435
436             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
437                                  bsr->VolParams[i].LastIndex, fd);
438             if (count) {
439                fprintf(fd, "Count=%u\n", count);
440             }
441             total_count += count;
442             /* If the same file is present on two tapes or in two files
443              *   on a tape, it is a continuation, and should not be treated
444              *   twice in the totals.
445              */
446             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
447                total_count--;
448             }
449             first = false;
450             LastIndex = bsr->VolParams[i].LastIndex;
451          }
452       }
453    }
454    return total_count;
455 }
456
457 void print_bsr(UAContext *ua, RBSR *bsr)
458 {
459    for ( ; bsr; bsr=bsr->next) {
460       for (int i=0; i < bsr->VolCount; i++) {
461          bsendmsg(ua, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
462          bsendmsg(ua, "MediaType\"%s\"\n", bsr->VolParams[i].MediaType);
463          bsendmsg(ua, "VolSessionId=%u\n", bsr->VolSessionId);
464          bsendmsg(ua, "VolSessionTime=%u\n", bsr->VolSessionTime);
465          bsendmsg(ua, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile,
466                   bsr->VolParams[i].EndFile);
467          bsendmsg(ua, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock,
468                   bsr->VolParams[i].EndBlock);
469          print_findex(ua, bsr->fi);
470       }
471       print_bsr(ua, bsr->next);
472    }
473 }
474
475
476
477
478 /*
479  * Add a FileIndex to the list of BootStrap records.
480  *  Here we are only dealing with JobId's and the FileIndexes
481  *  associated with those JobIds.
482  */
483 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
484 {
485    RBSR *nbsr;
486    RBSR_FINDEX *fi, *lfi;
487
488    if (findex == 0) {
489       return;                         /* probably a dummy directory */
490    }
491
492    if (bsr->fi == NULL) {             /* if no FI add one */
493       /* This is the first FileIndex item in the chain */
494       bsr->fi = new_findex();
495       bsr->JobId = JobId;
496       bsr->fi->findex = findex;
497       bsr->fi->findex2 = findex;
498       return;
499    }
500    /* Walk down list of bsrs until we find the JobId */
501    if (bsr->JobId != JobId) {
502       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
503          if (nbsr->JobId == JobId) {
504             bsr = nbsr;
505             break;
506          }
507       }
508
509       if (!nbsr) {                    /* Must add new JobId */
510          /* Add new JobId at end of chain */
511          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
512             {  }
513          nbsr->next = new_bsr();
514          nbsr->next->JobId = JobId;
515          nbsr->next->fi = new_findex();
516          nbsr->next->fi->findex = findex;
517          nbsr->next->fi->findex2 = findex;
518          return;
519       }
520    }
521
522    /*
523     * At this point, bsr points to bsr containing this JobId,
524     *  and we are sure that there is at least one fi record.
525     */
526    lfi = fi = bsr->fi;
527    /* Check if this findex is smaller than first item */
528    if (findex < fi->findex) {
529       if ((findex+1) == fi->findex) {
530          fi->findex = findex;         /* extend down */
531          return;
532       }
533       fi = new_findex();              /* yes, insert before first item */
534       fi->findex = findex;
535       fi->findex2 = findex;
536       fi->next = lfi;
537       bsr->fi = fi;
538       return;
539    }
540    /* Walk down fi chain and find where to insert insert new FileIndex */
541    for ( ; fi; fi=fi->next) {
542       if (findex == (fi->findex2 + 1)) {  /* extend up */
543          RBSR_FINDEX *nfi;
544          fi->findex2 = findex;
545          /*
546           * If the following record contains one higher, merge its
547           *   file index by extending it up.
548           */
549          if (fi->next && ((findex+1) == fi->next->findex)) {
550             nfi = fi->next;
551             fi->findex2 = nfi->findex2;
552             fi->next = nfi->next;
553             free(nfi);
554          }
555          return;
556       }
557       if (findex < fi->findex) {      /* add before */
558          if ((findex+1) == fi->findex) {
559             fi->findex = findex;
560             return;
561          }
562          break;
563       }
564       lfi = fi;
565    }
566    /* Add to last place found */
567    fi = new_findex();
568    fi->findex = findex;
569    fi->findex2 = findex;
570    fi->next = lfi->next;
571    lfi->next = fi;
572    return;
573 }
574
575 /*
576  * Add all possible  FileIndexes to the list of BootStrap records.
577  *  Here we are only dealing with JobId's and the FileIndexes
578  *  associated with those JobIds.
579  */
580 void add_findex_all(RBSR *bsr, uint32_t JobId)
581 {
582    RBSR *nbsr;
583    RBSR_FINDEX *fi;
584
585    if (bsr->fi == NULL) {             /* if no FI add one */
586       /* This is the first FileIndex item in the chain */
587       bsr->fi = new_findex();
588       bsr->JobId = JobId;
589       bsr->fi->findex = 1;
590       bsr->fi->findex2 = INT32_MAX;
591       return;
592    }
593    /* Walk down list of bsrs until we find the JobId */
594    if (bsr->JobId != JobId) {
595       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
596          if (nbsr->JobId == JobId) {
597             bsr = nbsr;
598             break;
599          }
600       }
601
602       if (!nbsr) {                    /* Must add new JobId */
603          /* Add new JobId at end of chain */
604          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
605             {  }
606          nbsr->next = new_bsr();
607          nbsr->next->JobId = JobId;
608          nbsr->next->fi = new_findex();
609          nbsr->next->fi->findex = 1;
610          nbsr->next->fi->findex2 = INT32_MAX;
611          return;
612       }
613    }
614
615    /*
616     * At this point, bsr points to bsr containing this JobId,
617     *  and we are sure that there is at least one fi record.
618     */
619    fi = bsr->fi;
620    fi->findex = 1;
621    fi->findex2 = INT32_MAX;
622    return;
623 }