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