]> git.sur5r.net Git - bacula/bacula/blob - dird/bsr.c
Make out of freespace non-fatal for removable devices -- i.e. behaves like tape
[bacula/bacula] / 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  * Here we actually write out the details of the bsr file.
327  *  Note, there is one bsr for each JobId, but the bsr may
328  *  have multiple volumes, which have been entered in the
329  *  order they were written.  
330  * The bsrs must be written out in the order the JobIds
331  *  are found in the jobid list.
332  */
333 static uint32_t write_bsr(UAContext *ua, RESTORE_CTX &rx, FILE *fd)
334 {
335    char ed1[50], ed2[50];
336    uint32_t count = 0;
337    uint32_t total_count = 0;
338    uint32_t LastIndex = 0;
339    bool first = true;
340    char *p;
341    JobId_t JobId;
342    char device[MAX_NAME_LENGTH];
343    RBSR *bsr;
344    if (*rx.JobIds == 0) {
345       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
346          /*
347           * For a given volume, loop over all the JobMedia records.
348           *   VolCount is the number of JobMedia records.
349           */
350          for (int i=0; i < bsr->VolCount; i++) {
351             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
352                  bsr->VolParams[i].LastIndex)) {
353                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
354                continue;
355             }
356             if (!rx.store) {
357                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
358                                              bsr->VolParams[i].MediaType);
359             }
360             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
361             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
362             if (bsr->fileregex) {
363                fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
364             }
365             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
366                fprintf(fd, "Device=\"%s\"\n", device);
367             }
368             if (bsr->VolParams[i].Slot > 0) {
369                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
370             }
371             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
372             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
373             fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
374                     edit_uint64(bsr->VolParams[i].EndAddr, ed2));
375    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
376    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
377
378             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
379                                  bsr->VolParams[i].LastIndex, fd);
380             if (count) {
381                fprintf(fd, "Count=%u\n", count);
382             }
383             total_count += count;
384             /* If the same file is present on two tapes or in two files
385              *   on a tape, it is a continuation, and should not be treated
386              *   twice in the totals.
387              */
388             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
389                total_count--;
390             }
391             first = false;
392             LastIndex = bsr->VolParams[i].LastIndex;
393          }
394       }
395       return total_count;
396    }
397    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
398       for (bsr=rx.bsr; bsr; bsr=bsr->next) {
399          if (JobId != bsr->JobId) {
400             continue;
401          }
402          /*
403           * For a given volume, loop over all the JobMedia records.
404           *   VolCount is the number of JobMedia records.
405           */
406          for (int i=0; i < bsr->VolCount; i++) {
407             if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex,
408                  bsr->VolParams[i].LastIndex)) {
409                bsr->VolParams[i].VolumeName[0] = 0;  /* zap VolumeName */
410                continue;
411             }
412             if (!rx.store) {
413                find_storage_resource(ua, rx, bsr->VolParams[i].Storage,
414                                              bsr->VolParams[i].MediaType);
415             }
416             fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName);
417             fprintf(fd, "MediaType=\"%s\"\n", bsr->VolParams[i].MediaType);
418             if (bsr->fileregex) {
419                fprintf(fd, "FileRegex=%s\n", bsr->fileregex);
420             }
421             if (get_storage_device(device, bsr->VolParams[i].Storage)) {
422                fprintf(fd, "Device=\"%s\"\n", device);
423             }
424             if (bsr->VolParams[i].Slot > 0) {
425                fprintf(fd, "Slot=%d\n", bsr->VolParams[i].Slot);
426             }
427             fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId);
428             fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime);
429             fprintf(fd, "VolAddr=%s-%s\n", edit_uint64(bsr->VolParams[i].StartAddr, ed1),
430                     edit_uint64(bsr->VolParams[i].EndAddr, ed2));
431    //       Dmsg2(100, "bsr VolParam FI=%u LI=%u\n",
432    //          bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex);
433
434             count = write_findex(bsr->fi, bsr->VolParams[i].FirstIndex,
435                                  bsr->VolParams[i].LastIndex, fd);
436             if (count) {
437                fprintf(fd, "Count=%u\n", count);
438             }
439             total_count += count;
440             /* If the same file is present on two tapes or in two files
441              *   on a tape, it is a continuation, and should not be treated
442              *   twice in the totals.
443              */
444             if (!first && LastIndex == bsr->VolParams[i].FirstIndex) {
445                total_count--;
446             }
447             first = false;
448             LastIndex = bsr->VolParams[i].LastIndex;
449          }
450       }
451    }
452    return total_count;
453 }
454
455 void print_bsr(UAContext *ua, RESTORE_CTX &rx)
456 {
457    write_bsr(ua, rx, stdout);
458 }
459
460
461
462
463 /*
464  * Add a FileIndex to the list of BootStrap records.
465  *  Here we are only dealing with JobId's and the FileIndexes
466  *  associated with those JobIds.
467  * We expect that JobId, FileIndex are sorted ascending.
468  */
469 void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex)
470 {
471    RBSR *nbsr;
472    RBSR_FINDEX *fi, *lfi;
473
474    if (findex == 0) {
475       return;                         /* probably a dummy directory */
476    }
477
478    if (bsr->fi == NULL) {             /* if no FI add one */
479       /* This is the first FileIndex item in the chain */
480       bsr->fi = new_findex();
481       bsr->JobId = JobId;
482       bsr->fi->findex = findex;
483       bsr->fi->findex2 = findex;
484       return;
485    }
486    /* Walk down list of bsrs until we find the JobId */
487    if (bsr->JobId != JobId) {
488       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
489          if (nbsr->JobId == JobId) {
490             bsr = nbsr;
491             break;
492          }
493       }
494
495       if (!nbsr) {                    /* Must add new JobId */
496          /* Add new JobId at end of chain */
497          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
498             {  }
499          nbsr->next = new_bsr();
500          nbsr->next->JobId = JobId;
501          nbsr->next->fi = new_findex();
502          nbsr->next->fi->findex = findex;
503          nbsr->next->fi->findex2 = findex;
504          return;
505       }
506    }
507
508    /*
509     * At this point, bsr points to bsr containing this JobId,
510     *  and we are sure that there is at least one fi record.
511     */
512    lfi = fi = bsr->fi;
513    /* Check if this findex is smaller than first item */
514    if (findex < fi->findex) {
515       if ((findex+1) == fi->findex) {
516          fi->findex = findex;         /* extend down */
517          return;
518       }
519       fi = new_findex();              /* yes, insert before first item */
520       fi->findex = findex;
521       fi->findex2 = findex;
522       fi->next = lfi;
523       bsr->fi = fi;
524       return;
525    }
526    /* Walk down fi chain and find where to insert insert new FileIndex */
527    for ( ; fi; fi=fi->next) {
528       if (findex == (fi->findex2 + 1)) {  /* extend up */
529          RBSR_FINDEX *nfi;
530          fi->findex2 = findex;
531          /*
532           * If the following record contains one higher, merge its
533           *   file index by extending it up.
534           */
535          if (fi->next && ((findex+1) == fi->next->findex)) {
536             nfi = fi->next;
537             fi->findex2 = nfi->findex2;
538             fi->next = nfi->next;
539             free(nfi);
540          }
541          return;
542       }
543       if (findex < fi->findex) {      /* add before */
544          if ((findex+1) == fi->findex) {
545             fi->findex = findex;
546             return;
547          }
548          break;
549       }
550       lfi = fi;
551    }
552    /* Add to last place found */
553    fi = new_findex();
554    fi->findex = findex;
555    fi->findex2 = findex;
556    fi->next = lfi->next;
557    lfi->next = fi;
558    return;
559 }
560
561 /*
562  * Add all possible  FileIndexes to the list of BootStrap records.
563  *  Here we are only dealing with JobId's and the FileIndexes
564  *  associated with those JobIds.
565  */
566 void add_findex_all(RBSR *bsr, uint32_t JobId)
567 {
568    RBSR *nbsr;
569    RBSR_FINDEX *fi;
570
571    if (bsr->fi == NULL) {             /* if no FI add one */
572       /* This is the first FileIndex item in the chain */
573       bsr->fi = new_findex();
574       bsr->JobId = JobId;
575       bsr->fi->findex = 1;
576       bsr->fi->findex2 = INT32_MAX;
577       return;
578    }
579    /* Walk down list of bsrs until we find the JobId */
580    if (bsr->JobId != JobId) {
581       for (nbsr=bsr->next; nbsr; nbsr=nbsr->next) {
582          if (nbsr->JobId == JobId) {
583             bsr = nbsr;
584             break;
585          }
586       }
587
588       if (!nbsr) {                    /* Must add new JobId */
589          /* Add new JobId at end of chain */
590          for (nbsr=bsr; nbsr->next; nbsr=nbsr->next)
591             {  }
592          nbsr->next = new_bsr();
593          nbsr->next->JobId = JobId;
594          nbsr->next->fi = new_findex();
595          nbsr->next->fi->findex = 1;
596          nbsr->next->fi->findex2 = INT32_MAX;
597          return;
598       }
599    }
600
601    /*
602     * At this point, bsr points to bsr containing this JobId,
603     *  and we are sure that there is at least one fi record.
604     */
605    fi = bsr->fi;
606    fi->findex = 1;
607    fi->findex2 = INT32_MAX;
608    return;
609 }