2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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 two of the GNU 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 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.
40 #include "findlib/find.h"
42 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
44 static void do_extract(char *fname);
45 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
47 static DEVICE *dev = NULL;
52 static BSR *bsr = NULL;
53 static bool extract = false;
54 static int non_support_data = 0;
55 static long total = 0;
58 static uint32_t num_files = 0;
59 static uint32_t compress_buf_size = 70000;
60 static POOLMEM *compress_buf;
61 static int prog_name_msg = 0;
62 static int win32_data_msg = 0;
63 static char *VolumeName = NULL;
65 static char *wbuf; /* write buffer address */
66 static uint32_t wsize; /* write size */
67 static uint64_t fileAddr = 0; /* file write address */
69 static CONFIG *config;
70 #define CONFIG_FILE "bacula-sd.conf"
71 char *configfile = NULL;
72 STORES *me = NULL; /* our Global resource */
73 bool forge_on = false;
74 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
75 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
81 "\nVersion: %s (%s)\n\n"
82 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
83 " -b <file> specify a bootstrap file\n"
84 " -c <file> specify a Storage configuration file\n"
85 " -d <nn> set debug level to <nn>\n"
86 " -dt print timestamp in debug output\n"
87 " -e <file> exclude list\n"
88 " -i <file> include list\n"
89 " -p proceed inspite of I/O errors\n"
91 " -V <volumes> specify Volume names (separated by |)\n"
92 " -? print this message\n\n"), 2000, VERSION, BDATE);
97 int main (int argc, char *argv[])
102 bool got_inc = false;
104 setlocale(LC_ALL, "");
105 bindtextdomain("bacula", LOCALEDIR);
106 textdomain("bacula");
110 working_directory = "/tmp";
111 my_name_is(argc, argv, "bextract");
112 init_msg(NULL, NULL); /* setup message handler */
116 ff = init_find_files();
119 while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
121 case 'b': /* bootstrap file */
122 bsr = parse_bsr(NULL, optarg);
123 // dump_bsr(bsr, true);
126 case 'c': /* specify config file */
127 if (configfile != NULL) {
130 configfile = bstrdup(optarg);
133 case 'd': /* debug level */
134 if (*optarg == 't') {
135 dbg_timestamp = true;
137 debug_level = atoi(optarg);
138 if (debug_level <= 0) {
144 case 'e': /* exclude list */
145 if ((fd = fopen(optarg, "rb")) == NULL) {
147 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
148 optarg, be.bstrerror());
151 while (fgets(line, sizeof(line), fd) != NULL) {
152 strip_trailing_junk(line);
153 Dmsg1(900, "add_exclude %s\n", line);
154 add_fname_to_exclude_list(ff, line);
159 case 'i': /* include list */
160 if ((fd = fopen(optarg, "rb")) == NULL) {
162 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
163 optarg, be.bstrerror());
166 while (fgets(line, sizeof(line), fd) != NULL) {
167 strip_trailing_junk(line);
168 Dmsg1(900, "add_include %s\n", line);
169 add_fname_to_include_list(ff, 0, line);
183 case 'V': /* Volume name */
197 Pmsg0(0, _("Wrong number of arguments: \n"));
201 if (configfile == NULL) {
202 configfile = bstrdup(CONFIG_FILE);
205 config = new_config_parser();
206 parse_sd_config(config, configfile, M_ERROR_TERM);
208 if (!got_inc) { /* If no include file, */
209 add_fname_to_include_list(ff, 0, "/"); /* include everything */
219 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
222 if (win32_data_msg) {
223 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
226 term_include_exclude_files(ff);
231 static void do_extract(char *devname)
235 enable_backup_privileges(NULL, 1);
237 jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
241 dev = jcr->read_dcr->dev;
247 /* Make sure where directory exists and that it is a directory */
248 if (stat(where, &statp) < 0) {
250 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
251 where, be.bstrerror());
253 if (!S_ISDIR(statp.st_mode)) {
254 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
258 jcr->where = bstrdup(where);
259 attr = new_attr(jcr);
261 compress_buf = get_memory(compress_buf_size);
263 read_records(dcr, record_cb, mount_next_read_volume);
264 /* If output file is still open, it was the last one in the
265 * archive since we just hit an end of file, so close the file.
267 if (is_bopen(&bfd)) {
268 set_attributes(jcr, attr, &bfd);
275 printf(_("%u files restored.\n"), num_files);
279 static bool store_data(BFILE *bfd, char *data, const int32_t length)
281 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
282 set_portable_backup(bfd);
283 if (!processWin32BackupAPIBlock(bfd, data, length)) {
285 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
286 attr->ofname, be.bstrerror());
289 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
291 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
292 attr->ofname, be.bstrerror());
300 * Called here for each record from read_records()
302 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
307 if (rec->FileIndex < 0) {
308 return true; /* we don't want labels */
311 /* File Attributes stream */
313 switch (rec->Stream) {
314 case STREAM_UNIX_ATTRIBUTES:
315 case STREAM_UNIX_ATTRIBUTES_EX:
317 /* If extracting, it was from previous stream, so
318 * close the output file.
321 if (!is_bopen(&bfd)) {
322 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
324 set_attributes(jcr, attr, &bfd);
328 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
329 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
332 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
333 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
334 if (!is_restore_stream_supported(attr->data_stream)) {
335 if (!non_support_data++) {
336 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
337 stream_to_ascii(attr->data_stream));
343 build_attr_output_fnames(jcr, attr);
345 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
346 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
352 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
359 print_ls_output(jcr, attr);
364 set_attributes(jcr, attr, &bfd);
365 print_ls_output(jcr, attr);
373 /* Data stream and extracting */
374 case STREAM_FILE_DATA:
375 case STREAM_SPARSE_DATA:
376 case STREAM_WIN32_DATA:
379 if (rec->Stream == STREAM_SPARSE_DATA) {
382 wbuf = rec->data + SPARSE_FADDR_SIZE;
383 wsize = rec->data_len - SPARSE_FADDR_SIZE;
384 ser_begin(rec->data, SPARSE_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:
414 if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
418 wbuf = rec->data + SPARSE_FADDR_SIZE;
419 wsize = rec->data_len - SPARSE_FADDR_SIZE;
420 ser_begin(rec->data, SPARSE_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 ((stat=uncompress((Byte *)compress_buf, &compress_len,
438 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR)
440 /* The buffer size is too small, try with a bigger one */
441 compress_len = compress_len + (compress_len >> 1);
442 compress_buf = check_pool_memory_size(compress_buf,
446 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
451 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
452 store_data(&bfd, compress_buf, compress_len);
453 total += compress_len;
454 fileAddr += compress_len;
455 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
460 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
467 case STREAM_MD5_DIGEST:
468 case STREAM_SHA1_DIGEST:
469 case STREAM_SHA256_DIGEST:
470 case STREAM_SHA512_DIGEST:
473 case STREAM_SIGNED_DIGEST:
474 case STREAM_ENCRYPTED_SESSION_DATA:
475 // TODO landonf: Investigate crypto support in the storage daemon
478 case STREAM_PROGRAM_NAMES:
479 case STREAM_PROGRAM_DATA:
480 if (!prog_name_msg) {
481 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
487 /* If extracting, weird stream (not 1 or 2), close output file anyway */
489 if (!is_bopen(&bfd)) {
490 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
492 set_attributes(jcr, attr, &bfd);
495 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
503 /* Dummies to replace askdir.c */
504 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
505 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
506 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
507 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
508 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
509 bool dir_send_job_status(JCR *jcr) {return 1;}
512 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
514 DEVICE *dev = dcr->dev;
515 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
516 dcr->VolumeName, dev->print_name());
522 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
524 Dmsg0(100, "Fake dir_get_volume_info\n");
525 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
526 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
527 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);