2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
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.
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 Bacula® is a registered trademark of Kern Sibbald.
18 * Dumb program to extract files from a Bacula backup.
27 #include "findlib/find.h"
30 #include <lzo/lzoconf.h>
31 #include <lzo/lzo1x.h>
34 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
36 static void do_extract(char *fname);
37 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
39 static DEVICE *dev = NULL;
44 static BSR *bsr = NULL;
45 static bool extract = false;
46 static int non_support_data = 0;
47 static long total = 0;
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;
57 static char *wbuf; /* write buffer address */
58 static uint32_t wsize; /* write size */
59 static uint64_t fileAddr = 0; /* file write address */
61 static CONFIG *config;
62 #define CONFIG_FILE "bacula-sd.conf"
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;
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"
85 " -V <volumes> specify Volume names (separated by |)\n"
86 " -? print this message\n\n"), 2000, VERSION, BDATE);
91 int main (int argc, char *argv[])
98 setlocale(LC_ALL, "");
99 bindtextdomain("bacula", LOCALEDIR);
100 textdomain("bacula");
104 working_directory = "/tmp";
105 my_name_is(argc, argv, "bextract");
106 init_msg(NULL, NULL); /* setup message handler */
110 ff = init_find_files();
113 while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
115 case 'b': /* bootstrap file */
116 bsr = parse_bsr(NULL, optarg);
117 // dump_bsr(bsr, true);
120 case 'c': /* specify config file */
121 if (configfile != NULL) {
124 configfile = bstrdup(optarg);
127 case 'd': /* debug level */
128 if (*optarg == 't') {
129 dbg_timestamp = true;
131 debug_level = atoi(optarg);
132 if (debug_level <= 0) {
138 case 'e': /* exclude list */
139 if ((fd = fopen(optarg, "rb")) == NULL) {
141 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
142 optarg, be.bstrerror());
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);
153 case 'i': /* include list */
154 if ((fd = fopen(optarg, "rb")) == NULL) {
156 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
157 optarg, be.bstrerror());
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);
177 case 'V': /* Volume name */
191 Pmsg0(0, _("Wrong number of arguments: \n"));
195 if (configfile == NULL) {
196 configfile = bstrdup(CONFIG_FILE);
199 config = new_config_parser();
200 parse_sd_config(config, configfile, M_ERROR_TERM);
202 load_sd_plugins(me->plugin_directory);
204 if (!got_inc) { /* If no include file, */
205 add_fname_to_include_list(ff, 0, "/"); /* include everything */
215 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
218 if (win32_data_msg) {
219 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
222 term_include_exclude_files(ff);
227 static void do_extract(char *devname)
231 enable_backup_privileges(NULL, 1);
233 jcr = setup_jcr("bextract", devname, bsr, VolumeName, SD_READ);
237 dev = jcr->read_dcr->dev;
243 /* Make sure where directory exists and that it is a directory */
244 if (stat(where, &statp) < 0) {
246 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
247 where, be.bstrerror());
249 if (!S_ISDIR(statp.st_mode)) {
250 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
254 jcr->where = bstrdup(where);
255 attr = new_attr(jcr);
257 compress_buf = get_memory(compress_buf_size);
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.
263 if (is_bopen(&bfd)) {
264 set_attributes(jcr, attr, &bfd);
271 printf(_("%u files restored.\n"), num_files);
275 static bool store_data(BFILE *bfd, char *data, const int32_t length)
277 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
278 set_portable_backup(bfd);
279 if (!processWin32BackupAPIBlock(bfd, data, length)) {
281 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
282 attr->ofname, be.bstrerror());
285 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
287 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
288 attr->ofname, be.bstrerror());
296 * Called here for each record from read_records()
298 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
303 if (rec->FileIndex < 0) {
304 return true; /* we don't want labels */
307 /* File Attributes stream */
309 switch (rec->maskedStream) {
310 case STREAM_UNIX_ATTRIBUTES:
311 case STREAM_UNIX_ATTRIBUTES_EX:
313 /* If extracting, it was from previous stream, so
314 * close the output file.
317 if (!is_bopen(&bfd)) {
318 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
320 set_attributes(jcr, attr, &bfd);
324 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
325 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
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));
339 build_attr_output_fnames(jcr, attr);
341 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
342 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
348 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
355 print_ls_output(jcr, attr);
360 set_attributes(jcr, attr, &bfd);
361 print_ls_output(jcr, attr);
369 case STREAM_RESTORE_OBJECT:
373 /* Data stream and extracting */
374 case STREAM_FILE_DATA:
375 case STREAM_SPARSE_DATA:
376 case STREAM_WIN32_DATA:
379 if (rec->maskedStream == STREAM_SPARSE_DATA) {
382 wbuf = rec->data + OFFSET_FADDR_SIZE;
383 wsize = rec->data_len - OFFSET_FADDR_SIZE;
384 ser_begin(rec->data, OFFSET_FADDR_SIZE);
386 if (fileAddr != faddr) {
388 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
390 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
391 attr->ofname, be.bstrerror());
396 wsize = rec->data_len;
399 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
400 store_data(&bfd, wbuf, wsize);
405 /* GZIP data stream */
406 case STREAM_GZIP_DATA:
407 case STREAM_SPARSE_GZIP_DATA:
408 case STREAM_WIN32_GZIP_DATA:
411 uLong compress_len = compress_buf_size;
412 int stat = Z_BUF_ERROR;
414 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
418 wbuf = rec->data + OFFSET_FADDR_SIZE;
419 wsize = rec->data_len - OFFSET_FADDR_SIZE;
420 ser_begin(rec->data, OFFSET_FADDR_SIZE);
422 if (fileAddr != faddr) {
424 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
426 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
427 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
434 wsize = rec->data_len;
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,
445 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
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,
459 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
466 /* Compressed data stream */
467 case STREAM_COMPRESSED_DATA:
468 case STREAM_SPARSE_COMPRESSED_DATA:
469 case STREAM_WIN32_COMPRESSED_DATA:
471 uint32_t comp_magic, comp_len;
472 uint16_t comp_level, comp_version;
474 lzo_uint compress_len;
475 const unsigned char *cbuf;
476 int r, real_compress_len;
479 if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
483 wbuf = rec->data + OFFSET_FADDR_SIZE;
484 wsize = rec->data_len - OFFSET_FADDR_SIZE;
485 ser_begin(rec->data, OFFSET_FADDR_SIZE);
487 if (fileAddr != faddr) {
489 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
491 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
492 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
499 wsize = rec->data_len;
502 /* read compress header */
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);
513 if (comp_version != COMP_HEAD_VERSION) {
514 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
518 if (comp_len + sizeof(comp_stream_header) != wsize) {
519 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
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)
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,
541 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
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);
553 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
561 case STREAM_MD5_DIGEST:
562 case STREAM_SHA1_DIGEST:
563 case STREAM_SHA256_DIGEST:
564 case STREAM_SHA512_DIGEST:
567 case STREAM_SIGNED_DIGEST:
568 case STREAM_ENCRYPTED_SESSION_DATA:
569 // TODO landonf: Investigate crypto support in the storage daemon
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"));
581 /* If extracting, weird stream (not 1 or 2), close output file anyway */
583 if (!is_bopen(&bfd)) {
584 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
586 set_attributes(jcr, attr, &bfd);
589 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
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;}
606 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
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());
616 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
618 Dmsg0(100, "Fake dir_get_volume_info\n");
619 dcr->setVolCatName(dcr->VolumeName);
621 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
623 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);