2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Dumb program to extract files from a Bacula backup.
31 #include "findlib/find.h"
34 #include <lzo/lzoconf.h>
35 #include <lzo/lzo1x.h>
38 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
40 static void do_extract(char *fname);
41 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
43 static DEVICE *dev = NULL;
48 static BSR *bsr = NULL;
49 static bool extract = false;
50 static int non_support_data = 0;
51 static long total = 0;
53 static POOLMEM *curr_fname;
55 static uint64_t num_errors = 0;
56 static uint64_t num_records = 0;
57 static uint32_t num_files = 0;
58 static uint32_t compress_buf_size = 70000;
59 static POOLMEM *compress_buf;
60 static int prog_name_msg = 0;
61 static int win32_data_msg = 0;
62 static char *VolumeName = NULL;
64 static char *wbuf; /* write buffer address */
65 static uint32_t wsize; /* write size */
66 static uint64_t fileAddr = 0; /* file write address */
68 static CONFIG *config;
69 #define CONFIG_FILE "bacula-sd.conf"
72 char *configfile = NULL;
73 STORES *me = NULL; /* our Global resource */
74 bool forge_on = false;
75 bool skip_extract = false;
76 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
77 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
83 "\n%sVersion: %s (%s)\n\n"
84 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
85 " -b <file> specify a bootstrap file\n"
86 " -c <file> specify a Storage configuration file\n"
87 " -d <nn> set debug level to <nn>\n"
88 " -dt print timestamp in debug output\n"
89 " -T send debug traces to trace file (stored in /tmp)\n"
90 " -e <file> exclude list\n"
91 " -i <file> include list\n"
92 " -p proceed inspite of I/O errors\n"
93 " -t read data from volume, do not write anything\n"
95 " -V <volumes> specify Volume names (separated by |)\n"
96 " -? print this message\n\n"), 2000, "", VERSION, BDATE);
101 int main (int argc, char *argv[])
106 bool got_inc = false;
108 setlocale(LC_ALL, "");
109 bindtextdomain("bacula", LOCALEDIR);
110 textdomain("bacula");
114 working_directory = "/tmp";
115 my_name_is(argc, argv, "bextract");
116 init_msg(NULL, NULL); /* setup message handler */
120 ff = init_find_files();
123 while ((ch = getopt(argc, argv, "Ttb:c:d:e:i:pvV:?")) != -1) {
129 case 'b': /* bootstrap file */
130 bsr = parse_bsr(NULL, optarg);
131 // dump_bsr(bsr, true);
134 case 'T': /* Send debug to trace file */
138 case 'c': /* specify config file */
139 if (configfile != NULL) {
142 configfile = bstrdup(optarg);
145 case 'd': /* debug level */
146 if (*optarg == 't') {
147 dbg_timestamp = true;
149 debug_level = atoi(optarg);
150 if (debug_level <= 0) {
156 case 'e': /* exclude list */
157 if ((fd = fopen(optarg, "rb")) == NULL) {
159 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
160 optarg, be.bstrerror());
163 while (fgets(line, sizeof(line), fd) != NULL) {
164 strip_trailing_junk(line);
165 Dmsg1(900, "add_exclude %s\n", line);
166 add_fname_to_exclude_list(ff, line);
171 case 'i': /* include list */
172 if ((fd = fopen(optarg, "rb")) == NULL) {
174 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
175 optarg, be.bstrerror());
178 while (fgets(line, sizeof(line), fd) != NULL) {
179 strip_trailing_junk(line);
180 Dmsg1(900, "add_include %s\n", line);
181 add_fname_to_include_list(ff, 0, line);
195 case 'V': /* Volume name */
209 Pmsg0(0, _("Wrong number of arguments: \n"));
213 if (configfile == NULL) {
214 configfile = bstrdup(CONFIG_FILE);
217 config = new_config_parser();
218 parse_sd_config(config, configfile, M_ERROR_TERM);
220 load_sd_plugins(me->plugin_directory);
222 if (!got_inc) { /* If no include file, */
223 add_fname_to_include_list(ff, 0, "/"); /* include everything */
233 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
236 if (win32_data_msg) {
237 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
240 term_include_exclude_files(ff);
245 static void do_extract(char *devname)
250 enable_backup_privileges(NULL, 1);
252 jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ, false/*read dedup data*/);
256 dev = jcr->read_dcr->dev;
262 /* Make sure where directory exists and that it is a directory */
263 if (stat(where, &statp) < 0) {
265 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
266 where, be.bstrerror());
268 if (!S_ISDIR(statp.st_mode)) {
269 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
273 jcr->where = bstrdup(where);
274 attr = new_attr(jcr);
276 compress_buf = get_memory(compress_buf_size);
277 curr_fname = get_pool_memory(PM_FNAME);
280 read_records(dcr, record_cb, mount_next_read_volume);
281 /* If output file is still open, it was the last one in the
282 * archive since we just hit an end of file, so close the file.
284 if (is_bopen(&bfd)) {
285 set_attributes(jcr, attr, &bfd);
291 free_pool_memory(curr_fname);
293 printf(_("%u files restored.\n"), num_files);
295 printf(_("Found %s error%s\n"), edit_uint64(num_errors, ed1), num_errors>1? "s":"");
300 static bool store_data(BFILE *bfd, char *data, const int32_t length)
302 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
303 set_portable_backup(bfd);
304 if (!processWin32BackupAPIBlock(bfd, data, length)) {
306 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
307 attr->ofname, be.bstrerror());
310 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
312 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
313 attr->ofname, be.bstrerror());
321 * Called here for each record from read_records()
323 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
329 bool restoredatap = false;
330 POOLMEM *orgdata = NULL;
331 uint32_t orgdata_len = 0;
333 if (rec->FileIndex < 0) {
334 return true; /* we don't want labels */
337 /* In this mode, we do not create any file on disk, just read
338 * everything from the volume.
341 switch (rec->maskedStream) {
342 case STREAM_UNIX_ATTRIBUTES:
343 case STREAM_UNIX_ATTRIBUTES_EX:
344 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
345 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
348 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
349 build_attr_output_fnames(jcr, attr);
350 print_ls_output(jcr, attr);
352 pm_strcpy(curr_fname, attr->fname);
358 /* We display some progress information if verbose not set or set to 2 */
359 if (verbose != 1 && (num_records % 200000) == 0L) {
360 fprintf(stderr, "\rfiles=%d records=%s\n", num_files, edit_uint64(num_records, ed1));
366 /* File Attributes stream */
368 switch (rec->maskedStream) {
369 case STREAM_UNIX_ATTRIBUTES:
370 case STREAM_UNIX_ATTRIBUTES_EX:
372 /* If extracting, it was from previous stream, so
373 * close the output file.
376 if (!is_bopen(&bfd)) {
377 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
379 set_attributes(jcr, attr, &bfd);
383 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
384 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
387 /* Keep the name of the current file if we find a bad block */
388 pm_strcpy(curr_fname, attr->fname);
390 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
391 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
392 if (!is_restore_stream_supported(attr->data_stream)) {
393 if (!non_support_data++) {
394 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
395 stream_to_ascii(attr->data_stream));
401 build_attr_output_fnames(jcr, attr);
403 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
404 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
410 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
418 print_ls_output(jcr, attr);
423 set_attributes(jcr, attr, &bfd);
424 print_ls_output(jcr, attr);
432 case STREAM_RESTORE_OBJECT:
436 /* Data stream and extracting */
437 case STREAM_FILE_DATA:
438 case STREAM_SPARSE_DATA:
439 case STREAM_WIN32_DATA:
442 if (rec->maskedStream == STREAM_SPARSE_DATA) {
445 wbuf = rec->data + OFFSET_FADDR_SIZE;
446 wsize = rec->data_len - OFFSET_FADDR_SIZE;
447 ser_begin(rec->data, OFFSET_FADDR_SIZE);
449 if (fileAddr != faddr) {
451 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
453 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
454 attr->ofname, be.bstrerror());
459 wsize = rec->data_len;
462 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
463 store_data(&bfd, wbuf, wsize);
468 /* GZIP data stream */
469 case STREAM_GZIP_DATA:
470 case STREAM_SPARSE_GZIP_DATA:
471 case STREAM_WIN32_GZIP_DATA:
474 uLong compress_len = compress_buf_size;
475 int stat = Z_BUF_ERROR;
477 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
481 wbuf = rec->data + OFFSET_FADDR_SIZE;
482 wsize = rec->data_len - OFFSET_FADDR_SIZE;
483 ser_begin(rec->data, OFFSET_FADDR_SIZE);
485 if (fileAddr != faddr) {
487 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
489 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
490 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
497 wsize = rec->data_len;
500 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
501 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
502 /* The buffer size is too small, try with a bigger one */
503 compress_len = 2 * compress_len;
504 compress_buf = check_pool_memory_size(compress_buf,
508 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
513 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
514 store_data(&bfd, compress_buf, compress_len);
515 total += compress_len;
516 fileAddr += compress_len;
517 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
522 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
529 /* Compressed data stream */
530 case STREAM_COMPRESSED_DATA:
531 case STREAM_SPARSE_COMPRESSED_DATA:
532 case STREAM_WIN32_COMPRESSED_DATA:
534 uint32_t comp_magic, comp_len;
535 uint16_t comp_level, comp_version;
537 lzo_uint compress_len;
538 const unsigned char *cbuf;
539 int r, real_compress_len;
542 if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
546 wbuf = rec->data + OFFSET_FADDR_SIZE;
547 wsize = rec->data_len - OFFSET_FADDR_SIZE;
548 ser_begin(rec->data, OFFSET_FADDR_SIZE);
550 if (fileAddr != faddr) {
552 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
554 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
555 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
562 wsize = rec->data_len;
565 /* read compress header */
567 unser_begin(wbuf, sizeof(comp_stream_header));
568 unser_uint32(comp_magic);
569 unser_uint32(comp_len);
570 unser_uint16(comp_level);
571 unser_uint16(comp_version);
572 Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
573 comp_level, comp_version);
576 if (comp_version != COMP_HEAD_VERSION) {
577 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
582 if (comp_len + sizeof(comp_stream_header) != wsize) {
583 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
592 compress_len = compress_buf_size;
593 cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
594 real_compress_len = wsize - sizeof(comp_stream_header);
595 Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
596 while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
597 (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
600 /* The buffer size is too small, try with a bigger one */
601 compress_len = 2 * compress_len;
602 compress_buf = check_pool_memory_size(compress_buf,
606 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
610 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
611 store_data(&bfd, compress_buf, compress_len);
612 total += compress_len;
613 fileAddr += compress_len;
614 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
618 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
626 case STREAM_MD5_DIGEST:
627 case STREAM_SHA1_DIGEST:
628 case STREAM_SHA256_DIGEST:
629 case STREAM_SHA512_DIGEST:
632 case STREAM_SIGNED_DIGEST:
633 case STREAM_ENCRYPTED_SESSION_DATA:
634 // TODO landonf: Investigate crypto support in the storage daemon
637 case STREAM_PROGRAM_NAMES:
638 case STREAM_PROGRAM_DATA:
639 if (!prog_name_msg) {
640 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
646 /* If extracting, weird stream (not 1 or 2), close output file anyway */
648 if (!is_bopen(&bfd)) {
649 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
651 set_attributes(jcr, attr, &bfd);
654 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
662 rec->data_len = orgdata_len;
667 /* Dummies to replace askdir.c */
668 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
669 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
670 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
671 bool flush_jobmedia_queue(JCR *jcr) { return true; }
672 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
673 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
674 bool dir_send_job_status(JCR *jcr) {return 1;}
677 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
679 DEVICE *dev = dcr->dev;
680 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
681 dcr->VolumeName, dev->print_name());
687 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
689 Dmsg0(100, "Fake dir_get_volume_info\n");
690 dcr->setVolCatName(dcr->VolumeName);
691 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);