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 John Walker.
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 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 #define CONFIG_FILE "bacula-sd.conf"
68 char *configfile = NULL;
69 STORES *me = NULL; /* our Global resource */
70 bool forge_on = false;
71 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
72 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
78 "\nVersion: %s (%s)\n\n"
79 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
80 " -b <file> specify a bootstrap file\n"
81 " -c <file> specify a configuration file\n"
82 " -d <nn> set debug level to <nn>\n"
83 " -dt print timestamp in debug output\n"
84 " -e <file> exclude list\n"
85 " -i <file> include list\n"
86 " -p proceed inspite of I/O errors\n"
88 " -V <volumes> specify Volume names (separated by |)\n"
89 " -? print this message\n\n"), 2000, VERSION, BDATE);
94 int main (int argc, char *argv[])
101 setlocale(LC_ALL, "");
102 bindtextdomain("bacula", LOCALEDIR);
103 textdomain("bacula");
106 working_directory = "/tmp";
107 my_name_is(argc, argv, "bextract");
108 init_msg(NULL, NULL); /* setup message handler */
112 ff = init_find_files();
115 while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
117 case 'b': /* bootstrap file */
118 bsr = parse_bsr(NULL, optarg);
119 // dump_bsr(bsr, true);
122 case 'c': /* specify config file */
123 if (configfile != NULL) {
126 configfile = bstrdup(optarg);
129 case 'd': /* debug level */
130 if (*optarg == 't') {
131 dbg_timestamp = true;
133 debug_level = atoi(optarg);
134 if (debug_level <= 0) {
140 case 'e': /* exclude list */
141 if ((fd = fopen(optarg, "rb")) == NULL) {
143 Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
144 optarg, be.bstrerror());
147 while (fgets(line, sizeof(line), fd) != NULL) {
148 strip_trailing_junk(line);
149 Dmsg1(900, "add_exclude %s\n", line);
150 add_fname_to_exclude_list(ff, line);
155 case 'i': /* include list */
156 if ((fd = fopen(optarg, "rb")) == NULL) {
158 Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
159 optarg, be.bstrerror());
162 while (fgets(line, sizeof(line), fd) != NULL) {
163 strip_trailing_junk(line);
164 Dmsg1(900, "add_include %s\n", line);
165 add_fname_to_include_list(ff, 0, line);
179 case 'V': /* Volume name */
193 Pmsg0(0, _("Wrong number of arguments: \n"));
197 if (configfile == NULL) {
198 configfile = bstrdup(CONFIG_FILE);
201 parse_config(configfile);
203 if (!got_inc) { /* If no include file, */
204 add_fname_to_include_list(ff, 0, "/"); /* include everything */
214 Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
217 if (win32_data_msg) {
218 Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
221 term_include_exclude_files(ff);
226 static void do_extract(char *devname)
230 enable_backup_privileges(NULL, 1);
232 jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
236 dev = jcr->read_dcr->dev;
242 /* Make sure where directory exists and that it is a directory */
243 if (stat(where, &statp) < 0) {
245 Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
246 where, be.bstrerror());
248 if (!S_ISDIR(statp.st_mode)) {
249 Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
253 jcr->where = bstrdup(where);
254 attr = new_attr(jcr);
256 compress_buf = get_memory(compress_buf_size);
258 read_records(dcr, record_cb, mount_next_read_volume);
259 /* If output file is still open, it was the last one in the
260 * archive since we just hit an end of file, so close the file.
262 if (is_bopen(&bfd)) {
263 set_attributes(jcr, attr, &bfd);
270 printf(_("%u files restored.\n"), num_files);
274 static bool store_data(BFILE *bfd, char *data, const int32_t length)
276 if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
277 set_portable_backup(bfd);
278 if (!processWin32BackupAPIBlock(bfd, data, length)) {
280 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
281 attr->ofname, be.bstrerror());
284 } else if (bwrite(bfd, data, length) != (ssize_t)length) {
286 Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
287 attr->ofname, be.bstrerror());
295 * Called here for each record from read_records()
297 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
302 if (rec->FileIndex < 0) {
303 return true; /* we don't want labels */
306 /* File Attributes stream */
308 switch (rec->Stream) {
309 case STREAM_UNIX_ATTRIBUTES:
310 case STREAM_UNIX_ATTRIBUTES_EX:
312 /* If extracting, it was from previous stream, so
313 * close the output file.
316 if (!is_bopen(&bfd)) {
317 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
319 set_attributes(jcr, attr, &bfd);
323 if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
324 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
327 if (attr->file_index != rec->FileIndex) {
328 Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
329 rec->FileIndex, attr->file_index);
332 if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
334 attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
335 if (!is_restore_stream_supported(attr->data_stream)) {
336 if (!non_support_data++) {
337 Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
338 stream_to_ascii(attr->data_stream));
344 build_attr_output_fnames(jcr, attr);
346 if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
347 Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
353 stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
360 print_ls_output(jcr, attr);
365 set_attributes(jcr, attr, &bfd);
366 print_ls_output(jcr, attr);
374 /* Data stream and extracting */
375 case STREAM_FILE_DATA:
376 case STREAM_SPARSE_DATA:
377 case STREAM_WIN32_DATA:
380 if (rec->Stream == STREAM_SPARSE_DATA) {
383 wbuf = rec->data + SPARSE_FADDR_SIZE;
384 wsize = rec->data_len - SPARSE_FADDR_SIZE;
385 ser_begin(rec->data, SPARSE_FADDR_SIZE);
387 if (fileAddr != faddr) {
389 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
391 Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
392 attr->ofname, be.bstrerror());
397 wsize = rec->data_len;
400 Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
401 store_data(&bfd, wbuf, wsize);
406 /* GZIP data stream */
407 case STREAM_GZIP_DATA:
408 case STREAM_SPARSE_GZIP_DATA:
409 case STREAM_WIN32_GZIP_DATA:
415 if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
419 wbuf = rec->data + SPARSE_FADDR_SIZE;
420 wsize = rec->data_len - SPARSE_FADDR_SIZE;
421 ser_begin(rec->data, SPARSE_FADDR_SIZE);
423 if (fileAddr != faddr) {
425 if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
427 Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
428 edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
435 wsize = rec->data_len;
437 compress_len = compress_buf_size;
438 if ((stat=uncompress((Bytef *)compress_buf, &compress_len,
439 (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
440 Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
445 Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
446 store_data(&bfd, compress_buf, compress_len);
447 total += compress_len;
448 fileAddr += compress_len;
449 Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
454 Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
461 case STREAM_MD5_DIGEST:
462 case STREAM_SHA1_DIGEST:
463 case STREAM_SHA256_DIGEST:
464 case STREAM_SHA512_DIGEST:
467 case STREAM_SIGNED_DIGEST:
468 case STREAM_ENCRYPTED_SESSION_DATA:
469 // TODO landonf: Investigate crypto support in the storage daemon
472 case STREAM_PROGRAM_NAMES:
473 case STREAM_PROGRAM_DATA:
474 if (!prog_name_msg) {
475 Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
481 /* If extracting, wierd stream (not 1 or 2), close output file anyway */
483 if (!is_bopen(&bfd)) {
484 Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
486 set_attributes(jcr, attr, &bfd);
489 Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
497 /* Dummies to replace askdir.c */
498 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
499 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
500 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
501 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
502 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
503 bool dir_send_job_status(JCR *jcr) {return 1;}
506 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
508 DEVICE *dev = dcr->dev;
509 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
510 dcr->VolumeName, dev->print_name());
516 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
518 Dmsg0(100, "Fake dir_get_volume_info\n");
519 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
520 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
521 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);