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 (attr->file_index != rec->FileIndex) {
333 Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
334 rec->FileIndex, attr->file_index);
337 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
339 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
340 if (!is_restore_stream_supported(attr->data_stream)) {
341 if (!non_support_data++) {
342 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
343 stream_to_ascii(attr->data_stream));
349 build_attr_output_fnames(jcr, attr);
351 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
352 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
358 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
365 print_ls_output(jcr, attr);
370 set_attributes(jcr, attr, &bfd);
371 print_ls_output(jcr, attr);
379 /* Data stream and extracting */
380 case STREAM_FILE_DATA:
381 case STREAM_SPARSE_DATA:
382 case STREAM_WIN32_DATA:
385 if (rec->Stream == STREAM_SPARSE_DATA) {
388 wbuf = rec->data + SPARSE_FADDR_SIZE;
389 wsize = rec->data_len - SPARSE_FADDR_SIZE;
390 ser_begin(rec->data, SPARSE_FADDR_SIZE);
392 if (fileAddr != faddr) {
394 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
396 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
397 attr->ofname, be.bstrerror());
402 wsize = rec->data_len;
405 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
406 store_data(&bfd, wbuf, wsize);
411 /* GZIP data stream */
412 case STREAM_GZIP_DATA:
413 case STREAM_SPARSE_GZIP_DATA:
414 case STREAM_WIN32_GZIP_DATA:
420 if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
424 wbuf = rec->data + SPARSE_FADDR_SIZE;
425 wsize = rec->data_len - SPARSE_FADDR_SIZE;
426 ser_begin(rec->data, SPARSE_FADDR_SIZE);
428 if (fileAddr != faddr) {
430 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
432 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
433 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
440 wsize = rec->data_len;
443 while ((stat=uncompress((Byte *)compress_buf, &compress_len,
444 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR)
446 /* The buffer size is too small, try with a bigger one */
447 compress_len = compress_len + (compress_len >> 1);
448 compress_buf = check_pool_memory_size(compress_buf,
452 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
457 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
458 store_data(&bfd, compress_buf, compress_len);
459 total += compress_len;
460 fileAddr += compress_len;
461 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
466 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
473 case STREAM_MD5_DIGEST:
474 case STREAM_SHA1_DIGEST:
475 case STREAM_SHA256_DIGEST:
476 case STREAM_SHA512_DIGEST:
479 case STREAM_SIGNED_DIGEST:
480 case STREAM_ENCRYPTED_SESSION_DATA:
481 // TODO landonf: Investigate crypto support in the storage daemon
484 case STREAM_PROGRAM_NAMES:
485 case STREAM_PROGRAM_DATA:
486 if (!prog_name_msg) {
487 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
493 /* If extracting, weird stream (not 1 or 2), close output file anyway */
495 if (!is_bopen(&bfd)) {
496 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
498 set_attributes(jcr, attr, &bfd);
501 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
509 /* Dummies to replace askdir.c */
510 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
511 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
512 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
513 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
514 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
515 bool dir_send_job_status(JCR *jcr) {return 1;}
518 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
520 DEVICE *dev = dcr->dev;
521 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
522 dcr->VolumeName, dev->print_name());
528 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
530 Dmsg0(100, "Fake dir_get_volume_info\n");
531 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
532 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
533 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);