2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Dumb program to extract files from a Bacula backup.
39 #include "findlib/find.h"
41 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
43 static void do_extract(char *fname);
44 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
46 static DEVICE *dev = NULL;
51 static BSR *bsr = NULL;
52 static bool extract = false;
53 static int non_support_data = 0;
54 static long total = 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"
70 char *configfile = NULL;
71 STORES *me = NULL; /* our Global resource */
72 bool forge_on = false;
73 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
74 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
80 "\nVersion: %s (%s)\n\n"
81 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
82 " -b <file> specify a bootstrap file\n"
83 " -c <file> specify a Storage configuration file\n"
84 " -d <nn> set debug level to <nn>\n"
85 " -dt print timestamp in debug output\n"
86 " -e <file> exclude list\n"
87 " -i <file> include list\n"
88 " -p proceed inspite of I/O errors\n"
90 " -V <volumes> specify Volume names (separated by |)\n"
91 " -? print this message\n\n"), 2000, VERSION, BDATE);
96 int main (int argc, char *argv[])
101 bool got_inc = false;
103 setlocale(LC_ALL, "");
104 bindtextdomain("bacula", LOCALEDIR);
105 textdomain("bacula");
109 working_directory = "/tmp";
110 my_name_is(argc, argv, "bextract");
111 init_msg(NULL, NULL); /* setup message handler */
115 ff = init_find_files();
118 while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
120 case 'b': /* bootstrap file */
121 bsr = parse_bsr(NULL, optarg);
122 // dump_bsr(bsr, true);
125 case 'c': /* specify config file */
126 if (configfile != NULL) {
129 configfile = bstrdup(optarg);
132 case 'd': /* debug level */
133 if (*optarg == 't') {
134 dbg_timestamp = true;
136 debug_level = atoi(optarg);
137 if (debug_level <= 0) {
143 case 'e': /* exclude list */
144 if ((fd = fopen(optarg, "rb")) == NULL) {
146 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
147 optarg, be.bstrerror());
150 while (fgets(line, sizeof(line), fd) != NULL) {
151 strip_trailing_junk(line);
152 Dmsg1(900, "add_exclude %s\n", line);
153 add_fname_to_exclude_list(ff, line);
158 case 'i': /* include list */
159 if ((fd = fopen(optarg, "rb")) == NULL) {
161 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
162 optarg, be.bstrerror());
165 while (fgets(line, sizeof(line), fd) != NULL) {
166 strip_trailing_junk(line);
167 Dmsg1(900, "add_include %s\n", line);
168 add_fname_to_include_list(ff, 0, line);
182 case 'V': /* Volume name */
196 Pmsg0(0, _("Wrong number of arguments: \n"));
200 if (configfile == NULL) {
201 configfile = bstrdup(CONFIG_FILE);
204 config = new_config_parser();
205 parse_sd_config(config, configfile, M_ERROR_TERM);
207 if (!got_inc) { /* If no include file, */
208 add_fname_to_include_list(ff, 0, "/"); /* include everything */
218 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
221 if (win32_data_msg) {
222 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
225 term_include_exclude_files(ff);
230 static void do_extract(char *devname)
234 enable_backup_privileges(NULL, 1);
236 jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
240 dev = jcr->read_dcr->dev;
246 /* Make sure where directory exists and that it is a directory */
247 if (stat(where, &statp) < 0) {
249 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
250 where, be.bstrerror());
252 if (!S_ISDIR(statp.st_mode)) {
253 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
257 jcr->where = bstrdup(where);
258 attr = new_attr(jcr);
260 compress_buf = get_memory(compress_buf_size);
262 read_records(dcr, record_cb, mount_next_read_volume);
263 /* If output file is still open, it was the last one in the
264 * archive since we just hit an end of file, so close the file.
266 if (is_bopen(&bfd)) {
267 set_attributes(jcr, attr, &bfd);
274 printf(_("%u files restored.\n"), num_files);
278 static bool store_data(BFILE *bfd, char *data, const int32_t length)
280 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
281 set_portable_backup(bfd);
282 if (!processWin32BackupAPIBlock(bfd, data, length)) {
284 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
285 attr->ofname, be.bstrerror());
288 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
290 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
291 attr->ofname, be.bstrerror());
299 * Called here for each record from read_records()
301 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
306 if (rec->FileIndex < 0) {
307 return true; /* we don't want labels */
310 /* File Attributes stream */
312 switch (rec->maskedStream) {
313 case STREAM_UNIX_ATTRIBUTES:
314 case STREAM_UNIX_ATTRIBUTES_EX:
316 /* If extracting, it was from previous stream, so
317 * close the output file.
320 if (!is_bopen(&bfd)) {
321 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
323 set_attributes(jcr, attr, &bfd);
327 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
328 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
331 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
332 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
333 if (!is_restore_stream_supported(attr->data_stream)) {
334 if (!non_support_data++) {
335 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
336 stream_to_ascii(attr->data_stream));
342 build_attr_output_fnames(jcr, attr);
344 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
345 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
351 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
358 print_ls_output(jcr, attr);
363 set_attributes(jcr, attr, &bfd);
364 print_ls_output(jcr, attr);
372 case STREAM_RESTORE_OBJECT:
376 /* Data stream and extracting */
377 case STREAM_FILE_DATA:
378 case STREAM_SPARSE_DATA:
379 case STREAM_WIN32_DATA:
382 if (rec->maskedStream == STREAM_SPARSE_DATA) {
385 wbuf = rec->data + OFFSET_FADDR_SIZE;
386 wsize = rec->data_len - OFFSET_FADDR_SIZE;
387 ser_begin(rec->data, OFFSET_FADDR_SIZE);
389 if (fileAddr != faddr) {
391 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
393 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
394 attr->ofname, be.bstrerror());
399 wsize = rec->data_len;
402 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
403 store_data(&bfd, wbuf, wsize);
408 /* GZIP data stream */
409 case STREAM_GZIP_DATA:
410 case STREAM_SPARSE_GZIP_DATA:
411 case STREAM_WIN32_GZIP_DATA:
414 uLong compress_len = compress_buf_size;
415 int stat = Z_BUF_ERROR;
417 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
421 wbuf = rec->data + OFFSET_FADDR_SIZE;
422 wsize = rec->data_len - OFFSET_FADDR_SIZE;
423 ser_begin(rec->data, OFFSET_FADDR_SIZE);
425 if (fileAddr != faddr) {
427 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
429 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
430 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
437 wsize = rec->data_len;
440 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
441 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
442 /* The buffer size is too small, try with a bigger one */
443 compress_len = 2 * compress_len;
444 compress_buf = check_pool_memory_size(compress_buf,
448 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
453 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
454 store_data(&bfd, compress_buf, compress_len);
455 total += compress_len;
456 fileAddr += compress_len;
457 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
462 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
469 /* Compressed data stream */
470 case STREAM_COMPRESSED_DATA:
471 case STREAM_SPARSE_COMPRESSED_DATA:
472 case STREAM_WIN32_COMPRESSED_DATA:
474 uint32_t comp_magic, comp_len;
475 uint16_t comp_level, comp_version;
477 lzo_uint compress_len;
478 const unsigned char *cbuf;
479 int r, real_compress_len;
482 if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_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 /* read compress header */
507 unser_begin(wbuf, sizeof(comp_stream_header));
508 unser_uint32(comp_magic);
509 unser_uint32(comp_len);
510 unser_uint16(comp_level);
511 unser_uint16(comp_version);
512 Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
513 comp_level, comp_version);
516 if (comp_version != COMP_HEAD_VERSION) {
517 Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
521 if (comp_len + sizeof(comp_stream_header) != wsize) {
522 Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
530 compress_len = compress_buf_size;
531 cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
532 real_compress_len = wsize - sizeof(comp_stream_header);
533 Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
534 while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
535 (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
538 /* The buffer size is too small, try with a bigger one */
539 compress_len = 2 * compress_len;
540 compress_buf = check_pool_memory_size(compress_buf,
544 Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
551 Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
556 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
557 store_data(&bfd, compress_buf, compress_len);
558 total += compress_len;
559 fileAddr += compress_len;
560 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
565 case STREAM_MD5_DIGEST:
566 case STREAM_SHA1_DIGEST:
567 case STREAM_SHA256_DIGEST:
568 case STREAM_SHA512_DIGEST:
571 case STREAM_SIGNED_DIGEST:
572 case STREAM_ENCRYPTED_SESSION_DATA:
573 // TODO landonf: Investigate crypto support in the storage daemon
576 case STREAM_PROGRAM_NAMES:
577 case STREAM_PROGRAM_DATA:
578 if (!prog_name_msg) {
579 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
585 /* If extracting, weird stream (not 1 or 2), close output file anyway */
587 if (!is_bopen(&bfd)) {
588 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
590 set_attributes(jcr, attr, &bfd);
593 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
601 /* Dummies to replace askdir.c */
602 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
603 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
604 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
605 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
606 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
607 bool dir_send_job_status(JCR *jcr) {return 1;}
610 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
612 DEVICE *dev = dcr->dev;
613 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
614 dcr->VolumeName, dev->print_name());
620 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
622 Dmsg0(100, "Fake dir_get_volume_info\n");
623 dcr->setVolCatName(dcr->VolumeName);
624 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
625 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);