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 if (line[0] == 0) { /* skip blank lines */
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 if (line[0] == 0) { /* skip blank lines */
186 Dmsg1(900, "add_include %s\n", line);
187 add_fname_to_include_list(ff, 0, line);
201 case 'V': /* Volume name */
215 Pmsg0(0, _("Wrong number of arguments: \n"));
219 if (configfile == NULL) {
220 configfile = bstrdup(CONFIG_FILE);
223 config = new_config_parser();
224 parse_sd_config(config, configfile, M_ERROR_TERM);
226 load_sd_plugins(me->plugin_directory);
228 if (!got_inc) { /* If no include file, */
229 add_fname_to_include_list(ff, 0, "/"); /* include everything */
239 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
242 if (win32_data_msg) {
243 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
246 term_include_exclude_files(ff);
251 static void do_extract(char *devname)
256 enable_backup_privileges(NULL, 1);
258 jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ, false/*read dedup data*/);
262 dev = jcr->read_dcr->dev;
268 /* Make sure where directory exists and that it is a directory */
269 if (stat(where, &statp) < 0) {
271 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
272 where, be.bstrerror());
274 if (!S_ISDIR(statp.st_mode)) {
275 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
279 jcr->where = bstrdup(where);
280 attr = new_attr(jcr);
282 compress_buf = get_memory(compress_buf_size);
283 curr_fname = get_pool_memory(PM_FNAME);
286 read_records(dcr, record_cb, mount_next_read_volume);
287 /* If output file is still open, it was the last one in the
288 * archive since we just hit an end of file, so close the file.
290 if (is_bopen(&bfd)) {
291 set_attributes(jcr, attr, &bfd);
297 free_pool_memory(curr_fname);
299 printf(_("%u files restored.\n"), num_files);
301 printf(_("Found %s error%s\n"), edit_uint64(num_errors, ed1), num_errors>1? "s":"");
306 static bool store_data(BFILE *bfd, char *data, const int32_t length)
308 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
309 set_portable_backup(bfd);
310 if (!processWin32BackupAPIBlock(bfd, data, length)) {
312 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
313 attr->ofname, be.bstrerror());
316 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
318 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
319 attr->ofname, be.bstrerror());
327 * Called here for each record from read_records()
329 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
335 bool restoredatap = false;
336 POOLMEM *orgdata = NULL;
337 uint32_t orgdata_len = 0;
339 if (rec->FileIndex < 0) {
340 return true; /* we don't want labels */
343 /* In this mode, we do not create any file on disk, just read
344 * everything from the volume.
347 switch (rec->maskedStream) {
348 case STREAM_UNIX_ATTRIBUTES:
349 case STREAM_UNIX_ATTRIBUTES_EX:
350 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
351 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
354 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
355 build_attr_output_fnames(jcr, attr);
356 print_ls_output(jcr, attr);
358 pm_strcpy(curr_fname, attr->fname);
364 /* We display some progress information if verbose not set or set to 2 */
365 if (verbose != 1 && (num_records % 200000) == 0L) {
366 fprintf(stderr, "\rfiles=%d records=%s\n", num_files, edit_uint64(num_records, ed1));
372 /* File Attributes stream */
374 switch (rec->maskedStream) {
375 case STREAM_UNIX_ATTRIBUTES:
376 case STREAM_UNIX_ATTRIBUTES_EX:
378 /* If extracting, it was from previous stream, so
379 * close the output file.
382 if (!is_bopen(&bfd)) {
383 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
385 set_attributes(jcr, attr, &bfd);
389 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
390 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
393 /* Keep the name of the current file if we find a bad block */
394 pm_strcpy(curr_fname, attr->fname);
396 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
397 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
398 if (!is_restore_stream_supported(attr->data_stream)) {
399 if (!non_support_data++) {
400 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
401 stream_to_ascii(attr->data_stream));
407 build_attr_output_fnames(jcr, attr);
409 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
410 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
416 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
424 print_ls_output(jcr, attr);
429 set_attributes(jcr, attr, &bfd);
430 print_ls_output(jcr, attr);
438 case STREAM_RESTORE_OBJECT:
442 /* Data stream and extracting */
443 case STREAM_FILE_DATA:
444 case STREAM_SPARSE_DATA:
445 case STREAM_WIN32_DATA:
448 if (rec->maskedStream == STREAM_SPARSE_DATA) {
451 wbuf = rec->data + OFFSET_FADDR_SIZE;
452 wsize = rec->data_len - OFFSET_FADDR_SIZE;
453 ser_begin(rec->data, OFFSET_FADDR_SIZE);
455 if (fileAddr != faddr) {
457 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
459 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
460 attr->ofname, be.bstrerror());
465 wsize = rec->data_len;
468 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
469 store_data(&bfd, wbuf, wsize);
474 /* GZIP data stream */
475 case STREAM_GZIP_DATA:
476 case STREAM_SPARSE_GZIP_DATA:
477 case STREAM_WIN32_GZIP_DATA:
480 uLong compress_len = compress_buf_size;
481 int stat = Z_BUF_ERROR;
483 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
487 wbuf = rec->data + OFFSET_FADDR_SIZE;
488 wsize = rec->data_len - OFFSET_FADDR_SIZE;
489 ser_begin(rec->data, OFFSET_FADDR_SIZE);
491 if (fileAddr != faddr) {
493 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
495 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
496 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
503 wsize = rec->data_len;
506 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
507 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
508 /* The buffer size is too small, try with a bigger one */
509 compress_len = 2 * compress_len;
510 compress_buf = check_pool_memory_size(compress_buf,
514 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
519 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
520 store_data(&bfd, compress_buf, compress_len);
521 total += compress_len;
522 fileAddr += compress_len;
523 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
528 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
535 /* Compressed data stream */
536 case STREAM_COMPRESSED_DATA:
537 case STREAM_SPARSE_COMPRESSED_DATA:
538 case STREAM_WIN32_COMPRESSED_DATA:
540 uint32_t comp_magic, comp_len;
541 uint16_t comp_level, comp_version;
543 lzo_uint compress_len;
544 const unsigned char *cbuf;
545 int r, real_compress_len;
548 if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
552 wbuf = rec->data + OFFSET_FADDR_SIZE;
553 wsize = rec->data_len - OFFSET_FADDR_SIZE;
554 ser_begin(rec->data, OFFSET_FADDR_SIZE);
556 if (fileAddr != faddr) {
558 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
560 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
561 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
568 wsize = rec->data_len;
571 /* read compress header */
573 unser_begin(wbuf, sizeof(comp_stream_header));
574 unser_uint32(comp_magic);
575 unser_uint32(comp_len);
576 unser_uint16(comp_level);
577 unser_uint16(comp_version);
578 Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
579 comp_level, comp_version);
582 if (comp_version != COMP_HEAD_VERSION) {
583 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
588 if (comp_len + sizeof(comp_stream_header) != wsize) {
589 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
598 compress_len = compress_buf_size;
599 cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
600 real_compress_len = wsize - sizeof(comp_stream_header);
601 Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
602 while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
603 (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
606 /* The buffer size is too small, try with a bigger one */
607 compress_len = 2 * compress_len;
608 compress_buf = check_pool_memory_size(compress_buf,
612 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
616 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
617 store_data(&bfd, compress_buf, compress_len);
618 total += compress_len;
619 fileAddr += compress_len;
620 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
624 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
632 case STREAM_MD5_DIGEST:
633 case STREAM_SHA1_DIGEST:
634 case STREAM_SHA256_DIGEST:
635 case STREAM_SHA512_DIGEST:
638 case STREAM_SIGNED_DIGEST:
639 case STREAM_ENCRYPTED_SESSION_DATA:
640 // TODO landonf: Investigate crypto support in the storage daemon
643 case STREAM_PROGRAM_NAMES:
644 case STREAM_PROGRAM_DATA:
645 if (!prog_name_msg) {
646 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
652 /* If extracting, weird stream (not 1 or 2), close output file anyway */
654 if (!is_bopen(&bfd)) {
655 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
657 set_attributes(jcr, attr, &bfd);
660 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
668 rec->data_len = orgdata_len;
673 /* Dummies to replace askdir.c */
674 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
675 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
676 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
677 bool flush_jobmedia_queue(JCR *jcr) { return true; }
678 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
679 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
680 bool dir_send_job_status(JCR *jcr) {return 1;}
683 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
685 DEVICE *dev = dcr->dev;
686 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
687 dcr->VolumeName, dev->print_name());
693 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
695 Dmsg0(100, "Fake dir_get_volume_info\n");
696 dcr->setVolCatName(dcr->VolumeName);
697 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);