2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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.
30 #include "findlib/find.h"
33 #include <lzo/lzoconf.h>
34 #include <lzo/lzo1x.h>
37 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
39 static void do_extract(char *fname);
40 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
42 static DEVICE *dev = NULL;
47 static BSR *bsr = NULL;
48 static bool extract = false;
49 static int non_support_data = 0;
50 static long total = 0;
52 static POOLMEM *curr_fname;
54 static uint64_t num_errors = 0;
55 static uint64_t num_records = 0;
56 static uint32_t num_files = 0;
57 static uint32_t compress_buf_size = 70000;
58 static POOLMEM *compress_buf;
59 static int prog_name_msg = 0;
60 static int win32_data_msg = 0;
61 static char *VolumeName = NULL;
63 static char *wbuf; /* write buffer address */
64 static uint32_t wsize; /* write size */
65 static uint64_t fileAddr = 0; /* file write address */
67 static CONFIG *config;
68 #define CONFIG_FILE "bacula-sd.conf"
71 char *configfile = NULL;
72 STORES *me = NULL; /* our Global resource */
73 bool forge_on = false;
74 bool skip_extract = false;
75 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
76 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
82 "\n%sVersion: %s (%s)\n\n"
83 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
84 " -b <file> specify a bootstrap file\n"
85 " -c <file> specify a Storage configuration file\n"
86 " -d <nn> set debug level to <nn>\n"
87 " -dt print timestamp in debug output\n"
88 " -T send debug traces to trace file (stored in /tmp)\n"
89 " -e <file> exclude list\n"
90 " -i <file> include list\n"
91 " -p proceed inspite of I/O errors\n"
92 " -t read data from volume, do not write anything\n"
94 " -V <volumes> specify Volume names (separated by |)\n"
95 " -? print this message\n\n"), 2000, "", VERSION, BDATE);
100 int main (int argc, char *argv[])
105 bool got_inc = false;
107 setlocale(LC_ALL, "");
108 bindtextdomain("bacula", LOCALEDIR);
109 textdomain("bacula");
113 working_directory = "/tmp";
114 my_name_is(argc, argv, "bextract");
115 init_msg(NULL, NULL); /* setup message handler */
119 ff = init_find_files();
122 while ((ch = getopt(argc, argv, "Ttb:c:d:e:i:pvV:?")) != -1) {
128 case 'b': /* bootstrap file */
129 bsr = parse_bsr(NULL, optarg);
130 // dump_bsr(bsr, true);
133 case 'T': /* Send debug to trace file */
137 case 'c': /* specify config file */
138 if (configfile != NULL) {
141 configfile = bstrdup(optarg);
144 case 'd': /* debug level */
145 if (*optarg == 't') {
146 dbg_timestamp = true;
148 debug_level = atoi(optarg);
149 if (debug_level <= 0) {
155 case 'e': /* exclude list */
156 if ((fd = fopen(optarg, "rb")) == NULL) {
158 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
159 optarg, be.bstrerror());
162 while (fgets(line, sizeof(line), fd) != NULL) {
163 strip_trailing_junk(line);
164 if (line[0] == 0) { /* skip blank lines */
167 Dmsg1(900, "add_exclude %s\n", line);
168 add_fname_to_exclude_list(ff, line);
173 case 'i': /* include list */
174 if ((fd = fopen(optarg, "rb")) == NULL) {
176 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
177 optarg, be.bstrerror());
180 while (fgets(line, sizeof(line), fd) != NULL) {
181 strip_trailing_junk(line);
182 if (line[0] == 0) { /* skip blank lines */
185 Dmsg1(900, "add_include %s\n", line);
186 add_fname_to_include_list(ff, 0, line);
200 case 'V': /* Volume name */
214 Pmsg0(0, _("Wrong number of arguments: \n"));
218 if (configfile == NULL) {
219 configfile = bstrdup(CONFIG_FILE);
222 config = new_config_parser();
223 parse_sd_config(config, configfile, M_ERROR_TERM);
225 load_sd_plugins(me->plugin_directory);
227 if (!got_inc) { /* If no include file, */
228 add_fname_to_include_list(ff, 0, "/"); /* include everything */
238 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
241 if (win32_data_msg) {
242 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
245 term_include_exclude_files(ff);
250 static void do_extract(char *devname)
255 enable_backup_privileges(NULL, 1);
257 jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ, false/*read dedup data*/);
261 dev = jcr->read_dcr->dev;
267 /* Make sure where directory exists and that it is a directory */
268 if (stat(where, &statp) < 0) {
270 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
271 where, be.bstrerror());
273 if (!S_ISDIR(statp.st_mode)) {
274 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
278 jcr->where = bstrdup(where);
279 attr = new_attr(jcr);
281 compress_buf = get_memory(compress_buf_size);
282 curr_fname = get_pool_memory(PM_FNAME);
285 read_records(dcr, record_cb, mount_next_read_volume);
286 /* If output file is still open, it was the last one in the
287 * archive since we just hit an end of file, so close the file.
289 if (is_bopen(&bfd)) {
290 set_attributes(jcr, attr, &bfd);
296 free_pool_memory(curr_fname);
298 printf(_("%u files restored.\n"), num_files);
300 printf(_("Found %s error%s\n"), edit_uint64(num_errors, ed1), num_errors>1? "s":"");
305 static bool store_data(BFILE *bfd, char *data, const int32_t length)
307 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
308 set_portable_backup(bfd);
309 if (!processWin32BackupAPIBlock(bfd, data, length)) {
311 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
312 attr->ofname, be.bstrerror());
315 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
317 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
318 attr->ofname, be.bstrerror());
326 * Called here for each record from read_records()
328 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
334 bool restoredatap = false;
335 POOLMEM *orgdata = NULL;
336 uint32_t orgdata_len = 0;
338 if (rec->FileIndex < 0) {
339 return true; /* we don't want labels */
342 /* In this mode, we do not create any file on disk, just read
343 * everything from the volume.
346 switch (rec->maskedStream) {
347 case STREAM_UNIX_ATTRIBUTES:
348 case STREAM_UNIX_ATTRIBUTES_EX:
349 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
350 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
353 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
354 build_attr_output_fnames(jcr, attr);
355 print_ls_output(jcr, attr);
357 pm_strcpy(curr_fname, attr->fname);
363 /* We display some progress information if verbose not set or set to 2 */
364 if (verbose != 1 && (num_records % 200000) == 0L) {
365 fprintf(stderr, "\rfiles=%d records=%s\n", num_files, edit_uint64(num_records, ed1));
371 /* File Attributes stream */
373 switch (rec->maskedStream) {
374 case STREAM_UNIX_ATTRIBUTES:
375 case STREAM_UNIX_ATTRIBUTES_EX:
377 /* If extracting, it was from previous stream, so
378 * close the output file.
381 if (!is_bopen(&bfd)) {
382 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
384 set_attributes(jcr, attr, &bfd);
388 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
389 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
392 /* Keep the name of the current file if we find a bad block */
393 pm_strcpy(curr_fname, attr->fname);
395 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
396 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
397 if (!is_restore_stream_supported(attr->data_stream)) {
398 if (!non_support_data++) {
399 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
400 stream_to_ascii(attr->data_stream));
406 build_attr_output_fnames(jcr, attr);
408 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
409 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
415 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
423 print_ls_output(jcr, attr);
428 set_attributes(jcr, attr, &bfd);
429 print_ls_output(jcr, attr);
437 case STREAM_RESTORE_OBJECT:
441 /* Data stream and extracting */
442 case STREAM_FILE_DATA:
443 case STREAM_SPARSE_DATA:
444 case STREAM_WIN32_DATA:
447 if (rec->maskedStream == STREAM_SPARSE_DATA) {
450 wbuf = rec->data + OFFSET_FADDR_SIZE;
451 wsize = rec->data_len - OFFSET_FADDR_SIZE;
452 ser_begin(rec->data, OFFSET_FADDR_SIZE);
454 if (fileAddr != faddr) {
456 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
458 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
459 attr->ofname, be.bstrerror());
464 wsize = rec->data_len;
467 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
468 store_data(&bfd, wbuf, wsize);
473 /* GZIP data stream */
474 case STREAM_GZIP_DATA:
475 case STREAM_SPARSE_GZIP_DATA:
476 case STREAM_WIN32_GZIP_DATA:
479 uLong compress_len = compress_buf_size;
480 int stat = Z_BUF_ERROR;
482 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
486 wbuf = rec->data + OFFSET_FADDR_SIZE;
487 wsize = rec->data_len - OFFSET_FADDR_SIZE;
488 ser_begin(rec->data, OFFSET_FADDR_SIZE);
490 if (fileAddr != faddr) {
492 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
494 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
495 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
502 wsize = rec->data_len;
505 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
506 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
507 /* The buffer size is too small, try with a bigger one */
508 compress_len = 2 * compress_len;
509 compress_buf = check_pool_memory_size(compress_buf,
513 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
518 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
519 store_data(&bfd, compress_buf, compress_len);
520 total += compress_len;
521 fileAddr += compress_len;
522 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
527 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
534 /* Compressed data stream */
535 case STREAM_COMPRESSED_DATA:
536 case STREAM_SPARSE_COMPRESSED_DATA:
537 case STREAM_WIN32_COMPRESSED_DATA:
539 uint32_t comp_magic, comp_len;
540 uint16_t comp_level, comp_version;
542 lzo_uint compress_len;
543 const unsigned char *cbuf;
544 int r, real_compress_len;
547 if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
551 wbuf = rec->data + OFFSET_FADDR_SIZE;
552 wsize = rec->data_len - OFFSET_FADDR_SIZE;
553 ser_begin(rec->data, OFFSET_FADDR_SIZE);
555 if (fileAddr != faddr) {
557 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
559 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
560 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
567 wsize = rec->data_len;
570 /* read compress header */
572 unser_begin(wbuf, sizeof(comp_stream_header));
573 unser_uint32(comp_magic);
574 unser_uint32(comp_len);
575 unser_uint16(comp_level);
576 unser_uint16(comp_version);
577 Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
578 comp_level, comp_version);
581 if (comp_version != COMP_HEAD_VERSION) {
582 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
587 if (comp_len + sizeof(comp_stream_header) != wsize) {
588 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
597 compress_len = compress_buf_size;
598 cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
599 real_compress_len = wsize - sizeof(comp_stream_header);
600 Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
601 while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
602 (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
605 /* The buffer size is too small, try with a bigger one */
606 compress_len = 2 * compress_len;
607 compress_buf = check_pool_memory_size(compress_buf,
611 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
615 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
616 store_data(&bfd, compress_buf, compress_len);
617 total += compress_len;
618 fileAddr += compress_len;
619 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
623 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
631 case STREAM_MD5_DIGEST:
632 case STREAM_SHA1_DIGEST:
633 case STREAM_SHA256_DIGEST:
634 case STREAM_SHA512_DIGEST:
637 case STREAM_SIGNED_DIGEST:
638 case STREAM_ENCRYPTED_SESSION_DATA:
639 // TODO landonf: Investigate crypto support in the storage daemon
642 case STREAM_PROGRAM_NAMES:
643 case STREAM_PROGRAM_DATA:
644 if (!prog_name_msg) {
645 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
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;
672 /* Dummies to replace askdir.c */
673 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
674 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
675 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
676 bool flush_jobmedia_queue(JCR *jcr) { return true; }
677 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
678 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
679 bool dir_send_job_status(JCR *jcr) {return 1;}
682 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
684 DEVICE *dev = dcr->dev;
685 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
686 dcr->VolumeName, dev->print_name());
692 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
694 Dmsg0(100, "Fake dir_get_volume_info\n");
695 dcr->setVolCatName(dcr->VolumeName);
696 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);