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