]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
Fix bug #180 ERR=success in btape when tape error
[bacula/bacula] / bacula / src / stored / bextract.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *  Dumb program to extract files from a Bacula backup.
19  *
20  *   Kern E. Sibbald, MM
21  *
22  */
23
24 #include "bacula.h"
25 #include "stored.h"
26 #include "ch.h"
27 #include "findlib/find.h"
28
29 #ifdef HAVE_LZO
30 #include <lzo/lzoconf.h>
31 #include <lzo/lzo1x.h>
32 #endif
33
34 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
35
36 static void do_extract(char *fname);
37 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
38
39 static DEVICE *dev = NULL;
40 static DCR *dcr;
41 static BFILE bfd;
42 static JCR *jcr;
43 static FF_PKT *ff;
44 static BSR *bsr = NULL;
45 static bool extract = false;
46 static int non_support_data = 0;
47 static long total = 0;
48 static ATTR *attr;
49 static char *where;
50 static uint32_t num_files = 0;
51 static uint32_t compress_buf_size = 70000;
52 static POOLMEM *compress_buf;
53 static int prog_name_msg = 0;
54 static int win32_data_msg = 0;
55 static char *VolumeName = NULL;
56
57 static char *wbuf;                    /* write buffer address */
58 static uint32_t wsize;                /* write size */
59 static uint64_t fileAddr = 0;         /* file write address */
60
61 static CONFIG *config;
62 #define CONFIG_FILE "bacula-sd.conf"
63
64 void *start_heap;
65 char *configfile = NULL;
66 STORES *me = NULL;                    /* our Global resource */
67 bool forge_on = false;
68 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
70
71 static void usage()
72 {
73    fprintf(stderr, _(
74 PROG_COPYRIGHT
75 "\nVersion: %s (%s)\n\n"
76 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
77 "       -b <file>       specify a bootstrap file\n"
78 "       -c <file>       specify a Storage configuration file\n"
79 "       -d <nn>         set debug level to <nn>\n"
80 "       -dt             print timestamp in debug output\n"
81 "       -e <file>       exclude list\n"
82 "       -i <file>       include list\n"
83 "       -p              proceed inspite of I/O errors\n"
84 "       -v              verbose\n"
85 "       -V <volumes>    specify Volume names (separated by |)\n"
86 "       -?              print this message\n\n"), 2000, VERSION, BDATE);
87    exit(1);
88 }
89
90
91 int main (int argc, char *argv[])
92 {
93    int ch;
94    FILE *fd;
95    char line[1000];
96    bool got_inc = false;
97
98    setlocale(LC_ALL, "");
99    bindtextdomain("bacula", LOCALEDIR);
100    textdomain("bacula");
101    init_stack_dump();
102    lmgr_init_thread();
103
104    working_directory = "/tmp";
105    my_name_is(argc, argv, "bextract");
106    init_msg(NULL, NULL);              /* setup message handler */
107
108    OSDependentInit();
109
110    ff = init_find_files();
111    binit(&bfd);
112
113    while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
114       switch (ch) {
115       case 'b':                    /* bootstrap file */
116          bsr = parse_bsr(NULL, optarg);
117 //       dump_bsr(bsr, true);
118          break;
119
120       case 'c':                    /* specify config file */
121          if (configfile != NULL) {
122             free(configfile);
123          }
124          configfile = bstrdup(optarg);
125          break;
126
127       case 'd':                    /* debug level */
128          if (*optarg == 't') {
129             dbg_timestamp = true;
130          } else {
131             debug_level = atoi(optarg);
132             if (debug_level <= 0) {
133                debug_level = 1;
134             }
135          }
136          break;
137
138       case 'e':                    /* exclude list */
139          if ((fd = fopen(optarg, "rb")) == NULL) {
140             berrno be;
141             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
142                optarg, be.bstrerror());
143             exit(1);
144          }
145          while (fgets(line, sizeof(line), fd) != NULL) {
146             strip_trailing_junk(line);
147             Dmsg1(900, "add_exclude %s\n", line);
148             add_fname_to_exclude_list(ff, line);
149          }
150          fclose(fd);
151          break;
152
153       case 'i':                    /* include list */
154          if ((fd = fopen(optarg, "rb")) == NULL) {
155             berrno be;
156             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
157                optarg, be.bstrerror());
158             exit(1);
159          }
160          while (fgets(line, sizeof(line), fd) != NULL) {
161             strip_trailing_junk(line);
162             Dmsg1(900, "add_include %s\n", line);
163             add_fname_to_include_list(ff, 0, line);
164          }
165          fclose(fd);
166          got_inc = true;
167          break;
168
169       case 'p':
170          forge_on = true;
171          break;
172
173       case 'v':
174          verbose++;
175          break;
176
177       case 'V':                    /* Volume name */
178          VolumeName = optarg;
179          break;
180
181       case '?':
182       default:
183          usage();
184
185       } /* end switch */
186    } /* end while */
187    argc -= optind;
188    argv += optind;
189
190    if (argc != 2) {
191       Pmsg0(0, _("Wrong number of arguments: \n"));
192       usage();
193    }
194
195    if (configfile == NULL) {
196       configfile = bstrdup(CONFIG_FILE);
197    }
198
199    config = new_config_parser();
200    parse_sd_config(config, configfile, M_ERROR_TERM);
201    setup_me();
202    load_sd_plugins(me->plugin_directory);
203
204    if (!got_inc) {                            /* If no include file, */
205       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
206    }
207
208    where = argv[1];
209    do_extract(argv[0]);
210
211    if (bsr) {
212       free_bsr(bsr);
213    }
214    if (prog_name_msg) {
215       Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
216          prog_name_msg);
217    }
218    if (win32_data_msg) {
219       Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
220          win32_data_msg);
221    }
222    term_include_exclude_files(ff);
223    term_find_files(ff);
224    return 0;
225 }
226
227 static void do_extract(char *devname)
228 {
229    struct stat statp;
230
231    enable_backup_privileges(NULL, 1);
232
233    jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ);
234    if (!jcr) {
235       exit(1);
236    }
237    dev = jcr->read_dcr->dev;
238    if (!dev) {
239       exit(1);
240    }
241    dcr = jcr->read_dcr;
242
243    /* Make sure where directory exists and that it is a directory */
244    if (stat(where, &statp) < 0) {
245       berrno be;
246       Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
247          where, be.bstrerror());
248    }
249    if (!S_ISDIR(statp.st_mode)) {
250       Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
251    }
252
253    free(jcr->where);
254    jcr->where = bstrdup(where);
255    attr = new_attr(jcr);
256
257    compress_buf = get_memory(compress_buf_size);
258
259    read_records(dcr, record_cb, mount_next_read_volume);
260    /* If output file is still open, it was the last one in the
261     * archive since we just hit an end of file, so close the file.
262     */
263    if (is_bopen(&bfd)) {
264       set_attributes(jcr, attr, &bfd);
265    }
266    release_device(dcr);
267    free_attr(attr);
268    free_jcr(jcr);
269    dev->term();
270
271    printf(_("%u files restored.\n"), num_files);
272    return;
273 }
274
275 static bool store_data(BFILE *bfd, char *data, const int32_t length)
276 {
277    if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
278       set_portable_backup(bfd);
279       if (!processWin32BackupAPIBlock(bfd, data, length)) {
280          berrno be;
281          Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
282                attr->ofname, be.bstrerror());
283          return false;
284       }
285    } else if (bwrite(bfd, data, length) != (ssize_t)length) {
286       berrno be;
287       Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
288             attr->ofname, be.bstrerror());
289       return false;
290    }
291
292    return true;
293 }
294
295 /*
296  * Called here for each record from read_records()
297  */
298 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
299 {
300    int stat;
301    JCR *jcr = dcr->jcr;
302
303    if (rec->FileIndex < 0) {
304       return true;                    /* we don't want labels */
305    }
306
307    /* File Attributes stream */
308
309    switch (rec->maskedStream) {
310    case STREAM_UNIX_ATTRIBUTES:
311    case STREAM_UNIX_ATTRIBUTES_EX:
312
313       /* If extracting, it was from previous stream, so
314        * close the output file.
315        */
316       if (extract) {
317          if (!is_bopen(&bfd)) {
318             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
319          }
320          set_attributes(jcr, attr, &bfd);
321          extract = false;
322       }
323
324       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
325          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
326       }
327
328       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
329          attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
330          if (!is_restore_stream_supported(attr->data_stream)) {
331             if (!non_support_data++) {
332                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
333                   stream_to_ascii(attr->data_stream));
334             }
335             extract = false;
336             return true;
337          }
338
339          build_attr_output_fnames(jcr, attr);
340
341          if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
342             Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
343             extract = false;
344             return true;
345          }
346
347          extract = false;
348          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
349          switch (stat) {
350          case CF_ERROR:
351          case CF_SKIP:
352             break;
353          case CF_EXTRACT:
354             extract = true;
355             print_ls_output(jcr, attr);
356             num_files++;
357             fileAddr = 0;
358             break;
359          case CF_CREATED:
360             set_attributes(jcr, attr, &bfd);
361             print_ls_output(jcr, attr);
362             num_files++;
363             fileAddr = 0;
364             break;
365          }
366       }
367       break;
368
369    case STREAM_RESTORE_OBJECT:
370       /* nothing to do */
371       break;
372
373    /* Data stream and extracting */
374    case STREAM_FILE_DATA:
375    case STREAM_SPARSE_DATA:
376    case STREAM_WIN32_DATA:
377
378       if (extract) {
379          if (rec->maskedStream == STREAM_SPARSE_DATA) {
380             ser_declare;
381             uint64_t faddr;
382             wbuf = rec->data + OFFSET_FADDR_SIZE;
383             wsize = rec->data_len - OFFSET_FADDR_SIZE;
384             ser_begin(rec->data, OFFSET_FADDR_SIZE);
385             unser_uint64(faddr);
386             if (fileAddr != faddr) {
387                fileAddr = faddr;
388                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
389                   berrno be;
390                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
391                      attr->ofname, be.bstrerror());
392                }
393             }
394          } else {
395             wbuf = rec->data;
396             wsize = rec->data_len;
397          }
398          total += wsize;
399          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
400          store_data(&bfd, wbuf, wsize);
401          fileAddr += wsize;
402       }
403       break;
404
405    /* GZIP data stream */
406    case STREAM_GZIP_DATA:
407    case STREAM_SPARSE_GZIP_DATA:
408    case STREAM_WIN32_GZIP_DATA:
409 #ifdef HAVE_LIBZ
410       if (extract) {
411          uLong compress_len = compress_buf_size;
412          int stat = Z_BUF_ERROR;
413
414          if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
415             ser_declare;
416             uint64_t faddr;
417             char ec1[50];
418             wbuf = rec->data + OFFSET_FADDR_SIZE;
419             wsize = rec->data_len - OFFSET_FADDR_SIZE;
420             ser_begin(rec->data, OFFSET_FADDR_SIZE);
421             unser_uint64(faddr);
422             if (fileAddr != faddr) {
423                fileAddr = faddr;
424                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
425                   berrno be;
426                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
427                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
428                   extract = false;
429                   return true;
430                }
431             }
432          } else {
433             wbuf = rec->data;
434             wsize = rec->data_len;
435          }
436
437          while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
438                                  (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
439             /* The buffer size is too small, try with a bigger one */
440             compress_len = 2 * compress_len;
441             compress_buf = check_pool_memory_size(compress_buf,
442                                                   compress_len);
443          }
444          if (stat != Z_OK) {
445             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
446             extract = false;
447             return true;
448          }
449
450          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
451          store_data(&bfd, compress_buf, compress_len);
452          total += compress_len;
453          fileAddr += compress_len;
454          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
455             compress_len);
456       }
457 #else
458       if (extract) {
459          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
460          extract = false;
461          return true;
462       }
463 #endif
464       break;
465
466    /* Compressed data stream */
467    case STREAM_COMPRESSED_DATA:
468    case STREAM_SPARSE_COMPRESSED_DATA:
469    case STREAM_WIN32_COMPRESSED_DATA:
470       if (extract) {
471          uint32_t comp_magic, comp_len;
472          uint16_t comp_level, comp_version;
473 #ifdef HAVE_LZO
474          lzo_uint compress_len;
475          const unsigned char *cbuf;
476          int r, real_compress_len;
477 #endif
478
479          if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
480             ser_declare;
481             uint64_t faddr;
482             char ec1[50];
483             wbuf = rec->data + OFFSET_FADDR_SIZE;
484             wsize = rec->data_len - OFFSET_FADDR_SIZE;
485             ser_begin(rec->data, OFFSET_FADDR_SIZE);
486             unser_uint64(faddr);
487             if (fileAddr != faddr) {
488                fileAddr = faddr;
489                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
490                   berrno be;
491                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
492                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
493                   extract = false;
494                   return true;
495                }
496             }
497          } else {
498             wbuf = rec->data;
499             wsize = rec->data_len;
500          }
501
502          /* read compress header */
503          unser_declare;
504          unser_begin(wbuf, sizeof(comp_stream_header));
505          unser_uint32(comp_magic);
506          unser_uint32(comp_len);
507          unser_uint16(comp_level);
508          unser_uint16(comp_version);
509          Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
510                                  comp_level, comp_version);
511
512          /* version check */
513          if (comp_version != COMP_HEAD_VERSION) {
514             Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
515             return false;
516          }
517          /* size check */
518          if (comp_len + sizeof(comp_stream_header) != wsize) {
519             Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
520                  comp_len, wsize);
521             return false;
522          }
523
524           switch(comp_magic) {
525 #ifdef HAVE_LZO
526             case COMPRESS_LZO1X:
527                compress_len = compress_buf_size;
528                cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
529                real_compress_len = wsize - sizeof(comp_stream_header);
530                Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
531                while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
532                                                (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
533                {
534
535                   /* The buffer size is too small, try with a bigger one */
536                   compress_len = 2 * compress_len;
537                   compress_buf = check_pool_memory_size(compress_buf,
538                                                   compress_len);
539                }
540                if (r != LZO_E_OK) {
541                   Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
542                   extract = false;
543                   return true;
544                }
545                Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
546                store_data(&bfd, compress_buf, compress_len);
547                total += compress_len;
548                fileAddr += compress_len;
549                Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
550                break;
551 #endif
552             default:
553                Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
554                extract = false;
555                return true;
556          }
557
558       }
559       break;
560
561    case STREAM_MD5_DIGEST:
562    case STREAM_SHA1_DIGEST:
563    case STREAM_SHA256_DIGEST:
564    case STREAM_SHA512_DIGEST:
565       break;
566
567    case STREAM_SIGNED_DIGEST:
568    case STREAM_ENCRYPTED_SESSION_DATA:
569       // TODO landonf: Investigate crypto support in the storage daemon
570       break;
571
572    case STREAM_PROGRAM_NAMES:
573    case STREAM_PROGRAM_DATA:
574       if (!prog_name_msg) {
575          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
576          prog_name_msg++;
577       }
578       break;
579
580    default:
581       /* If extracting, weird stream (not 1 or 2), close output file anyway */
582       if (extract) {
583          if (!is_bopen(&bfd)) {
584             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
585          }
586          set_attributes(jcr, attr, &bfd);
587          extract = false;
588       }
589       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
590          rec->Stream);
591       break;
592
593    } /* end switch */
594    return true;
595 }
596
597 /* Dummies to replace askdir.c */
598 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
599 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
600 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
601 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
602 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
603 bool    dir_send_job_status(JCR *jcr) {return 1;}
604
605
606 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
607 {
608    DEVICE *dev = dcr->dev;
609    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
610       dcr->VolumeName, dev->print_name());
611    dev->close();
612    getchar();
613    return true;
614 }
615
616 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
617 {
618    Dmsg0(100, "Fake dir_get_volume_info\n");
619    dcr->setVolCatName(dcr->VolumeName);
620 #ifdef BUILD_DVD
621    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
622 #endif
623    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);
624    return 1;
625 }