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.
38 #include "findlib/find.h"
40 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
42 static void do_extract(char *fname);
43 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
45 static DEVICE *dev = NULL;
50 static BSR *bsr = NULL;
51 static bool extract = false;
52 static int non_support_data = 0;
53 static long total = 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"
69 char *configfile = NULL;
70 STORES *me = NULL; /* our Global resource */
71 bool forge_on = false;
72 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
73 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
79 "\nVersion: %s (%s)\n\n"
80 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
81 " -b <file> specify a bootstrap file\n"
82 " -c <file> specify a Storage configuration file\n"
83 " -d <nn> set debug level to <nn>\n"
84 " -dt print timestamp in debug output\n"
85 " -e <file> exclude list\n"
86 " -i <file> include list\n"
87 " -p proceed inspite of I/O errors\n"
89 " -V <volumes> specify Volume names (separated by |)\n"
90 " -? print this message\n\n"), 2000, VERSION, BDATE);
95 int main (int argc, char *argv[])
100 bool got_inc = false;
102 setlocale(LC_ALL, "");
103 bindtextdomain("bacula", LOCALEDIR);
104 textdomain("bacula");
108 working_directory = "/tmp";
109 my_name_is(argc, argv, "bextract");
110 init_msg(NULL, NULL); /* setup message handler */
114 ff = init_find_files();
117 while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
119 case 'b': /* bootstrap file */
120 bsr = parse_bsr(NULL, optarg);
121 // dump_bsr(bsr, true);
124 case 'c': /* specify config file */
125 if (configfile != NULL) {
128 configfile = bstrdup(optarg);
131 case 'd': /* debug level */
132 if (*optarg == 't') {
133 dbg_timestamp = true;
135 debug_level = atoi(optarg);
136 if (debug_level <= 0) {
142 case 'e': /* exclude list */
143 if ((fd = fopen(optarg, "rb")) == NULL) {
145 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
146 optarg, be.bstrerror());
149 while (fgets(line, sizeof(line), fd) != NULL) {
150 strip_trailing_junk(line);
151 Dmsg1(900, "add_exclude %s\n", line);
152 add_fname_to_exclude_list(ff, line);
157 case 'i': /* include list */
158 if ((fd = fopen(optarg, "rb")) == NULL) {
160 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
161 optarg, be.bstrerror());
164 while (fgets(line, sizeof(line), fd) != NULL) {
165 strip_trailing_junk(line);
166 Dmsg1(900, "add_include %s\n", line);
167 add_fname_to_include_list(ff, 0, line);
181 case 'V': /* Volume name */
195 Pmsg0(0, _("Wrong number of arguments: \n"));
199 if (configfile == NULL) {
200 configfile = bstrdup(CONFIG_FILE);
203 config = new_config_parser();
204 parse_sd_config(config, configfile, M_ERROR_TERM);
206 if (!got_inc) { /* If no include file, */
207 add_fname_to_include_list(ff, 0, "/"); /* include everything */
217 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
220 if (win32_data_msg) {
221 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
224 term_include_exclude_files(ff);
229 static void do_extract(char *devname)
233 enable_backup_privileges(NULL, 1);
235 jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
239 dev = jcr->read_dcr->dev;
245 /* Make sure where directory exists and that it is a directory */
246 if (stat(where, &statp) < 0) {
248 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
249 where, be.bstrerror());
251 if (!S_ISDIR(statp.st_mode)) {
252 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
256 jcr->where = bstrdup(where);
257 attr = new_attr(jcr);
259 compress_buf = get_memory(compress_buf_size);
261 read_records(dcr, record_cb, mount_next_read_volume);
262 /* If output file is still open, it was the last one in the
263 * archive since we just hit an end of file, so close the file.
265 if (is_bopen(&bfd)) {
266 set_attributes(jcr, attr, &bfd);
273 printf(_("%u files restored.\n"), num_files);
277 static bool store_data(BFILE *bfd, char *data, const int32_t length)
279 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
280 set_portable_backup(bfd);
281 if (!processWin32BackupAPIBlock(bfd, data, length)) {
283 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
284 attr->ofname, be.bstrerror());
287 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
289 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
290 attr->ofname, be.bstrerror());
298 * Called here for each record from read_records()
300 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
305 if (rec->FileIndex < 0) {
306 return true; /* we don't want labels */
309 /* File Attributes stream */
311 switch (rec->maskedStream) {
312 case STREAM_UNIX_ATTRIBUTES:
313 case STREAM_UNIX_ATTRIBUTES_EX:
315 /* If extracting, it was from previous stream, so
316 * close the output file.
319 if (!is_bopen(&bfd)) {
320 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
322 set_attributes(jcr, attr, &bfd);
326 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
327 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
330 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
331 attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
332 if (!is_restore_stream_supported(attr->data_stream)) {
333 if (!non_support_data++) {
334 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
335 stream_to_ascii(attr->data_stream));
341 build_attr_output_fnames(jcr, attr);
343 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
344 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
350 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
357 print_ls_output(jcr, attr);
362 set_attributes(jcr, attr, &bfd);
363 print_ls_output(jcr, attr);
371 case STREAM_RESTORE_OBJECT:
375 /* Data stream and extracting */
376 case STREAM_FILE_DATA:
377 case STREAM_SPARSE_DATA:
378 case STREAM_WIN32_DATA:
381 if (rec->maskedStream == STREAM_SPARSE_DATA) {
384 wbuf = rec->data + OFFSET_FADDR_SIZE;
385 wsize = rec->data_len - OFFSET_FADDR_SIZE;
386 ser_begin(rec->data, OFFSET_FADDR_SIZE);
388 if (fileAddr != faddr) {
390 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
392 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
393 attr->ofname, be.bstrerror());
398 wsize = rec->data_len;
401 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
402 store_data(&bfd, wbuf, wsize);
407 /* GZIP data stream */
408 case STREAM_GZIP_DATA:
409 case STREAM_SPARSE_GZIP_DATA:
410 case STREAM_WIN32_GZIP_DATA:
413 uLong compress_len = compress_buf_size;
414 int stat = Z_BUF_ERROR;
416 if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
420 wbuf = rec->data + OFFSET_FADDR_SIZE;
421 wsize = rec->data_len - OFFSET_FADDR_SIZE;
422 ser_begin(rec->data, OFFSET_FADDR_SIZE);
424 if (fileAddr != faddr) {
426 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
428 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
429 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
436 wsize = rec->data_len;
439 while (compress_len < 10000000 && (stat=uncompress((Byte *)compress_buf, &compress_len,
440 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
441 /* The buffer size is too small, try with a bigger one */
442 compress_len = 2 * compress_len;
443 compress_buf = check_pool_memory_size(compress_buf,
447 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
452 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
453 store_data(&bfd, compress_buf, compress_len);
454 total += compress_len;
455 fileAddr += compress_len;
456 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
461 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
468 case STREAM_MD5_DIGEST:
469 case STREAM_SHA1_DIGEST:
470 case STREAM_SHA256_DIGEST:
471 case STREAM_SHA512_DIGEST:
474 case STREAM_SIGNED_DIGEST:
475 case STREAM_ENCRYPTED_SESSION_DATA:
476 // TODO landonf: Investigate crypto support in the storage daemon
479 case STREAM_PROGRAM_NAMES:
480 case STREAM_PROGRAM_DATA:
481 if (!prog_name_msg) {
482 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
488 /* If extracting, weird stream (not 1 or 2), close output file anyway */
490 if (!is_bopen(&bfd)) {
491 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
493 set_attributes(jcr, attr, &bfd);
496 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
504 /* Dummies to replace askdir.c */
505 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
506 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
507 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
508 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
509 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
510 bool dir_send_job_status(JCR *jcr) {return 1;}
513 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
515 DEVICE *dev = dcr->dev;
516 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
517 dcr->VolumeName, dev->print_name());
523 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
525 Dmsg0(100, "Fake dir_get_volume_info\n");
526 dcr->setVolCatName(dcr->VolumeName);
527 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
528 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);