2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Dumb program to extract files from a Bacula backup.
29 #include "findlib/find.h"
32 #include <lzo/lzoconf.h>
33 #include <lzo/lzo1x.h>
36 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
38 static void do_extract(char *fname);
39 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
41 static DEVICE *dev = NULL;
46 static BSR *bsr = NULL;
47 static bool extract = false;
48 static int non_support_data = 0;
49 static long total = 0;
51 static POOLMEM *curr_fname;
53 static uint64_t num_errors = 0;
54 static uint64_t num_records = 0;
55 static uint32_t num_files = 0;
56 static uint32_t compress_buf_size = 70000;
57 static POOLMEM *compress_buf;
58 static int prog_name_msg = 0;
59 static int win32_data_msg = 0;
60 static char *VolumeName = NULL;
62 static char *wbuf; /* write buffer address */
63 static uint32_t wsize; /* write size */
64 static uint64_t fileAddr = 0; /* file write address */
66 static CONFIG *config;
67 #define CONFIG_FILE "bacula-sd.conf"
70 char *configfile = NULL;
71 bool skip_extract = false;
77 "\n%sVersion: %s (%s)\n\n"
78 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
79 " -b <file> specify a bootstrap file\n"
80 " -c <file> specify a Storage configuration file\n"
81 " -d <nn> set debug level to <nn>\n"
82 " -dt print timestamp in debug output\n"
83 " -T send debug traces to trace file (stored in /tmp)\n"
84 " -e <file> exclude list\n"
85 " -i <file> include list\n"
86 " -p proceed inspite of I/O errors\n"
87 " -t read data from volume, do not write anything\n"
89 " -V <volumes> specify Volume names (separated by |)\n"
90 " -? print this message\n\n"), 2000, "", VERSION, BDATE);
95 int main (int argc, char *argv[])
100 bool got_inc = false;
101 BtoolsAskDirHandler askdir_handler;
103 init_askdir_handler(&askdir_handler);
104 setlocale(LC_ALL, "");
105 bindtextdomain("bacula", LOCALEDIR);
106 textdomain("bacula");
110 working_directory = "/tmp";
111 my_name_is(argc, argv, "bextract");
112 init_msg(NULL, NULL); /* setup message handler */
116 ff = init_find_files();
119 while ((ch = getopt(argc, argv, "Ttb:c:d:e:i:pvV:?")) != -1) {
125 case 'b': /* bootstrap file */
126 bsr = parse_bsr(NULL, optarg);
129 case 'T': /* Send debug to trace file */
133 case 'c': /* specify config file */
134 if (configfile != NULL) {
137 configfile = bstrdup(optarg);
140 case 'd': /* debug level */
141 if (*optarg == 't') {
142 dbg_timestamp = true;
145 /* We probably find a tag list -d 10,sql,bvfs */
146 if ((p = strchr(optarg, ',')) != NULL) {
149 debug_level = atoi(optarg);
150 if (debug_level <= 0) {
154 debug_parse_tags(p+1, &debug_level_tags);
159 case 'e': /* exclude list */
160 if ((fd = fopen(optarg, "rb")) == NULL) {
162 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
163 optarg, be.bstrerror());
166 while (fgets(line, sizeof(line), fd) != NULL) {
167 strip_trailing_junk(line);
168 Dmsg1(900, "add_exclude %s\n", line);
169 add_fname_to_exclude_list(ff, line);
174 case 'i': /* include list */
175 if ((fd = fopen(optarg, "rb")) == NULL) {
177 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
178 optarg, be.bstrerror());
181 while (fgets(line, sizeof(line), fd) != NULL) {
182 strip_trailing_junk(line);
183 Dmsg1(900, "add_include %s\n", line);
184 add_fname_to_include_list(ff, 0, line);
198 case 'V': /* Volume name */
212 Pmsg0(0, _("Wrong number of arguments: \n"));
216 if (configfile == NULL) {
217 configfile = bstrdup(CONFIG_FILE);
220 config = New(CONFIG());
221 parse_sd_config(config, configfile, M_ERROR_TERM);
223 load_sd_plugins(me->plugin_directory);
225 if (!got_inc) { /* If no include file, */
226 add_fname_to_include_list(ff, 0, "/"); /* include everything */
236 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
239 if (win32_data_msg) {
240 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
243 term_include_exclude_files(ff);
248 static void do_extract(char *devname)
253 enable_backup_privileges(NULL, 1);
255 jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ, true/*read dedup data*/);
259 dev = jcr->read_dcr->dev;
265 /* Make sure where directory exists and that it is a directory */
266 if (stat(where, &statp) < 0) {
268 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
269 where, be.bstrerror());
271 if (!S_ISDIR(statp.st_mode)) {
272 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
276 jcr->where = bstrdup(where);
277 attr = new_attr(jcr);
279 compress_buf = get_memory(compress_buf_size);
280 curr_fname = get_pool_memory(PM_FNAME);
283 read_records(dcr, record_cb, mount_next_read_volume);
284 /* If output file is still open, it was the last one in the
285 * archive since we just hit an end of file, so close the file.
287 if (is_bopen(&bfd)) {
288 set_attributes(jcr, attr, &bfd);
294 free_pool_memory(curr_fname);
296 printf(_("%u files restored.\n"), num_files);
298 printf(_("Found %s error%s\n"), edit_uint64(num_errors, ed1), num_errors>1? "s":"");
303 static bool store_data(BFILE *bfd, char *data, const int32_t length)
305 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
306 set_portable_backup(bfd);
307 if (!processWin32BackupAPIBlock(bfd, data, length)) {
309 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
310 attr->ofname, be.bstrerror());
313 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
315 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
316 attr->ofname, be.bstrerror());
324 * Called here for each record from read_records()
326 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
332 bool restoredatap = false;
333 POOLMEM *orgdata = NULL;
334 uint32_t orgdata_len = 0;
336 if (rec->FileIndex < 0) {
337 return true; /* we don't want labels */
340 /* In this mode, we do not create any file on disk, just read
341 * everything from the volume.
344 switch (rec->maskedStream) {
345 case STREAM_UNIX_ATTRIBUTES:
346 case STREAM_UNIX_ATTRIBUTES_EX:
347 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
348 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
351 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
352 build_attr_output_fnames(jcr, attr);
353 print_ls_output(jcr, attr);
355 pm_strcpy(curr_fname, attr->fname);
361 /* We display some progress information if verbose not set or set to 2 */
362 if (verbose != 1 && (num_records % 200000) == 0L) {
363 fprintf(stderr, "\rfiles=%d records=%s\n", num_files, edit_uint64(num_records, ed1));
369 /* File Attributes stream */
371 switch (rec->maskedStream) {
372 case STREAM_UNIX_ATTRIBUTES:
373 case STREAM_UNIX_ATTRIBUTES_EX:
375 /* If extracting, it was from previous stream, so
376 * close the output file.
379 if (!is_bopen(&bfd)) {
380 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
382 set_attributes(jcr, attr, &bfd);
386 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
387 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
390 /* Keep the name of the current file if we find a bad block */
391 pm_strcpy(curr_fname, attr->fname);
393 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
394 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
395 if (!is_restore_stream_supported(attr->data_stream)) {
396 if (!non_support_data++) {
397 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
398 stream_to_ascii(attr->data_stream));
404 build_attr_output_fnames(jcr, attr);
406 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
407 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
413 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
421 print_ls_output(jcr, attr);
426 set_attributes(jcr, attr, &bfd);
427 print_ls_output(jcr, attr);
435 case STREAM_RESTORE_OBJECT:
439 /* Data stream and extracting */
440 case STREAM_FILE_DATA:
441 case STREAM_SPARSE_DATA:
442 case STREAM_WIN32_DATA:
444 if (rec->maskedStream == STREAM_SPARSE_DATA) {
447 wbuf = rec->data + OFFSET_FADDR_SIZE;
448 wsize = rec->data_len - OFFSET_FADDR_SIZE;
449 ser_begin(rec->data, OFFSET_FADDR_SIZE);
451 /* We seek only for real SPARSE data, not for OFFSET option */
452 if ((rec->Stream & STREAM_BIT_OFFSETS) == 0 && fileAddr != faddr) {
454 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
456 Emsg3(M_ERROR_TERM, 0, _("Seek error Addr=%llu on %s: %s\n"),
457 fileAddr, attr->ofname, be.bstrerror());
462 wsize = rec->data_len;
465 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
466 store_data(&bfd, wbuf, wsize);
471 /* GZIP data stream */
472 case STREAM_GZIP_DATA:
473 case STREAM_SPARSE_GZIP_DATA:
474 case STREAM_WIN32_GZIP_DATA:
477 uLong compress_len = compress_buf_size;
478 int stat = Z_BUF_ERROR;
480 if (rec->maskedStream == STREAM_SPARSE_DATA) {
484 wbuf = rec->data + OFFSET_FADDR_SIZE;
485 wsize = rec->data_len - OFFSET_FADDR_SIZE;
486 ser_begin(rec->data, OFFSET_FADDR_SIZE);
488 if ((rec->Stream & STREAM_BIT_OFFSETS) == 0 && fileAddr != faddr) {
490 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
492 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
493 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
500 wsize = rec->data_len;
503 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
504 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
505 /* The buffer size is too small, try with a bigger one */
506 compress_len = 2 * compress_len;
507 compress_buf = check_pool_memory_size(compress_buf,
511 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
516 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
517 store_data(&bfd, compress_buf, compress_len);
518 total += compress_len;
519 fileAddr += compress_len;
520 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
525 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
532 /* Compressed data stream */
533 case STREAM_COMPRESSED_DATA:
534 case STREAM_SPARSE_COMPRESSED_DATA:
535 case STREAM_WIN32_COMPRESSED_DATA:
537 uint32_t comp_magic, comp_len;
538 uint16_t comp_level, comp_version;
540 lzo_uint compress_len;
541 const unsigned char *cbuf;
542 int r, real_compress_len;
545 if (rec->maskedStream == STREAM_SPARSE_DATA) {
549 wbuf = rec->data + OFFSET_FADDR_SIZE;
550 wsize = rec->data_len - OFFSET_FADDR_SIZE;
551 ser_begin(rec->data, OFFSET_FADDR_SIZE);
553 if ((rec->Stream & STREAM_BIT_OFFSETS) == 0 && fileAddr != faddr) {
555 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
557 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
558 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
565 wsize = rec->data_len;
568 /* read compress header */
570 unser_begin(wbuf, sizeof(comp_stream_header));
571 unser_uint32(comp_magic);
572 unser_uint32(comp_len);
573 unser_uint16(comp_level);
574 unser_uint16(comp_version);
575 Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
576 comp_level, comp_version);
579 if (comp_version != COMP_HEAD_VERSION) {
580 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
585 if (comp_len + sizeof(comp_stream_header) != wsize) {
586 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
595 compress_len = compress_buf_size;
596 cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
597 real_compress_len = wsize - sizeof(comp_stream_header);
598 Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
599 while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
600 (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
603 /* The buffer size is too small, try with a bigger one */
604 compress_len = 2 * compress_len;
605 compress_buf = check_pool_memory_size(compress_buf,
609 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
613 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
614 store_data(&bfd, compress_buf, compress_len);
615 total += compress_len;
616 fileAddr += compress_len;
617 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
621 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
629 case STREAM_MD5_DIGEST:
630 case STREAM_SHA1_DIGEST:
631 case STREAM_SHA256_DIGEST:
632 case STREAM_SHA512_DIGEST:
635 case STREAM_SIGNED_DIGEST:
636 case STREAM_ENCRYPTED_SESSION_DATA:
637 // TODO landonf: Investigate crypto support in the storage daemon
640 case STREAM_PROGRAM_NAMES:
641 case STREAM_PROGRAM_DATA:
642 if (!prog_name_msg) {
643 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
647 case STREAM_PLUGIN_NAME:
648 /* Just ignore the plugin name */
651 /* If extracting, weird stream (not 1 or 2), close output file anyway */
653 if (!is_bopen(&bfd)) {
654 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
656 set_attributes(jcr, attr, &bfd);
659 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
667 rec->data_len = orgdata_len;