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