]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
Apply patch (with some difficulties) from Joao Henrique Freitas
[bacula/bacula] / bacula / src / stored / bextract.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *
30  *  Dumb program to extract files from a Bacula backup.
31  *
32  *   Kern E. Sibbald, MM
33  *
34  *   Version $Id$
35  *
36  */
37
38 #include "bacula.h"
39 #include "stored.h"
40 #include "findlib/find.h"
41
42 static void do_extract(char *fname);
43 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
44
45 static DEVICE *dev = NULL;
46 static DCR *dcr;
47 static BFILE bfd;
48 static JCR *jcr;
49 static FF_PKT *ff;
50 static BSR *bsr = NULL;
51 static bool extract = false;
52 static int non_support_data = 0;
53 static long total = 0;
54 static ATTR *attr;
55 static char *where;
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;
62
63 static char *wbuf;                    /* write buffer address */
64 static uint32_t wsize;                /* write size */
65 static uint64_t fileAddr = 0;         /* file write address */
66
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;
73
74 static void usage()
75 {
76    fprintf(stderr, _(
77 PROG_COPYRIGHT
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"
87 "       -v              verbose\n"
88 "       -V <volumes>    specify Volume names (separated by |)\n"
89 "       -?              print this message\n\n"), 2000, VERSION, BDATE);
90    exit(1);
91 }
92
93
94 int main (int argc, char *argv[])
95 {
96    int ch;
97    FILE *fd;
98    char line[1000];
99    bool got_inc = false;
100
101    setlocale(LC_ALL, "");
102    bindtextdomain("bacula", LOCALEDIR);
103    textdomain("bacula");
104    init_stack_dump();
105
106    working_directory = "/tmp";
107    my_name_is(argc, argv, "bextract");
108    init_msg(NULL, NULL);              /* setup message handler */
109
110    OSDependentInit();
111
112    ff = init_find_files();
113    binit(&bfd);
114
115    while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
116       switch (ch) {
117       case 'b':                    /* bootstrap file */
118          bsr = parse_bsr(NULL, optarg);
119 //       dump_bsr(bsr, true);
120          break;
121
122       case 'c':                    /* specify config file */
123          if (configfile != NULL) {
124             free(configfile);
125          }
126          configfile = bstrdup(optarg);
127          break;
128
129       case 'd':                    /* debug level */
130          if (*optarg == 't') {
131             dbg_timestamp = true;
132          } else {
133             debug_level = atoi(optarg);
134             if (debug_level <= 0) {
135                debug_level = 1;
136             }
137          }
138          break;
139
140       case 'e':                    /* exclude list */
141          if ((fd = fopen(optarg, "rb")) == NULL) {
142             berrno be;
143             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
144                optarg, be.bstrerror());
145             exit(1);
146          }
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);
151          }
152          fclose(fd);
153          break;
154
155       case 'i':                    /* include list */
156          if ((fd = fopen(optarg, "rb")) == NULL) {
157             berrno be;
158             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
159                optarg, be.bstrerror());
160             exit(1);
161          }
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);
166          }
167          fclose(fd);
168          got_inc = true;
169          break;
170
171       case 'p':
172          forge_on = true;
173          break;
174
175       case 'v':
176          verbose++;
177          break;
178
179       case 'V':                    /* Volume name */
180          VolumeName = optarg;
181          break;
182
183       case '?':
184       default:
185          usage();
186
187       } /* end switch */
188    } /* end while */
189    argc -= optind;
190    argv += optind;
191
192    if (argc != 2) {
193       Pmsg0(0, _("Wrong number of arguments: \n"));
194       usage();
195    }
196
197    if (configfile == NULL) {
198       configfile = bstrdup(CONFIG_FILE);
199    }
200
201    parse_config(configfile);
202
203    if (!got_inc) {                            /* If no include file, */
204       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
205    }
206
207    where = argv[1];
208    do_extract(argv[0]);
209
210    if (bsr) {
211       free_bsr(bsr);
212    }
213    if (prog_name_msg) {
214       Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
215          prog_name_msg);
216    }
217    if (win32_data_msg) {
218       Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
219          win32_data_msg);
220    }
221    term_include_exclude_files(ff);
222    term_find_files(ff);
223    return 0;
224 }
225
226 static void do_extract(char *devname)
227 {
228    struct stat statp;
229
230    enable_backup_privileges(NULL, 1);
231
232    jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
233    if (!jcr) {
234       exit(1);
235    }
236    dev = jcr->read_dcr->dev;
237    if (!dev) {
238       exit(1);
239    }
240    dcr = jcr->read_dcr;
241
242    /* Make sure where directory exists and that it is a directory */
243    if (stat(where, &statp) < 0) {
244       berrno be;
245       Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
246          where, be.bstrerror());
247    }
248    if (!S_ISDIR(statp.st_mode)) {
249       Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
250    }
251
252    free(jcr->where);
253    jcr->where = bstrdup(where);
254    attr = new_attr(jcr);
255
256    compress_buf = get_memory(compress_buf_size);
257
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.
261     */
262    if (is_bopen(&bfd)) {
263       set_attributes(jcr, attr, &bfd);
264    }
265    release_device(dcr);
266    free_attr(attr);
267    free_jcr(jcr);
268    dev->term();
269
270    printf(_("%u files restored.\n"), num_files);
271    return;
272 }
273
274 static bool store_data(BFILE *bfd, char *data, const int32_t length)
275 {
276    if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
277       set_portable_backup(bfd);
278       if (!processWin32BackupAPIBlock(bfd, data, length)) {
279          berrno be;
280          Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
281                attr->ofname, be.bstrerror());
282          return false;
283       }
284    } else if (bwrite(bfd, data, length) != (ssize_t)length) {
285       berrno be;
286       Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
287             attr->ofname, be.bstrerror());
288       return false;
289    }
290
291    return true;
292 }
293
294 /*
295  * Called here for each record from read_records()
296  */
297 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
298 {
299    int stat;
300    JCR *jcr = dcr->jcr;
301
302    if (rec->FileIndex < 0) {
303       return true;                    /* we don't want labels */
304    }
305
306    /* File Attributes stream */
307
308    switch (rec->Stream) {
309    case STREAM_UNIX_ATTRIBUTES:
310    case STREAM_UNIX_ATTRIBUTES_EX:
311
312       /* If extracting, it was from previous stream, so
313        * close the output file.
314        */
315       if (extract) {
316          if (!is_bopen(&bfd)) {
317             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
318          }
319          set_attributes(jcr, attr, &bfd);
320          extract = false;
321       }
322
323       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
324          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
325       }
326
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);
330       }
331
332       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
333
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));
339             }
340             extract = false;
341             return true;
342          }
343
344          build_attr_output_fnames(jcr, attr);
345
346          extract = false;
347          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
348          switch (stat) {
349          case CF_ERROR:
350          case CF_SKIP:
351             break;
352          case CF_EXTRACT:
353             extract = true;
354             print_ls_output(jcr, attr);
355             num_files++;
356             fileAddr = 0;
357             break;
358          case CF_CREATED:
359             set_attributes(jcr, attr, &bfd);
360             print_ls_output(jcr, attr);
361             num_files++;
362             fileAddr = 0;
363             break;
364          }
365       }
366       break;
367
368    /* Data stream and extracting */
369    case STREAM_FILE_DATA:
370    case STREAM_SPARSE_DATA:
371    case STREAM_WIN32_DATA:
372
373       if (extract) {
374          if (rec->Stream == STREAM_SPARSE_DATA) {
375             ser_declare;
376             uint64_t faddr;
377             wbuf = rec->data + SPARSE_FADDR_SIZE;
378             wsize = rec->data_len - SPARSE_FADDR_SIZE;
379             ser_begin(rec->data, SPARSE_FADDR_SIZE);
380             unser_uint64(faddr);
381             if (fileAddr != faddr) {
382                fileAddr = faddr;
383                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
384                   berrno be;
385                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
386                      attr->ofname, be.bstrerror());
387                }
388             }
389          } else {
390             wbuf = rec->data;
391             wsize = rec->data_len;
392          }
393          total += wsize;
394          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
395          store_data(&bfd, wbuf, wsize);
396          fileAddr += wsize;
397       }
398       break;
399
400    /* GZIP data stream */
401    case STREAM_GZIP_DATA:
402    case STREAM_SPARSE_GZIP_DATA:
403    case STREAM_WIN32_GZIP_DATA:
404 #ifdef HAVE_LIBZ
405       if (extract) {
406          uLong compress_len;
407          int stat;
408
409          if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
410             ser_declare;
411             uint64_t faddr;
412             char ec1[50];
413             wbuf = rec->data + SPARSE_FADDR_SIZE;
414             wsize = rec->data_len - SPARSE_FADDR_SIZE;
415             ser_begin(rec->data, SPARSE_FADDR_SIZE);
416             unser_uint64(faddr);
417             if (fileAddr != faddr) {
418                fileAddr = faddr;
419                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
420                   berrno be;
421                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
422                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
423                   extract = false;
424                   return true;
425                }
426             }
427          } else {
428             wbuf = rec->data;
429             wsize = rec->data_len;
430          }
431          compress_len = compress_buf_size;
432          if ((stat=uncompress((Bytef *)compress_buf, &compress_len,
433                (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
434             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
435             extract = false;
436             return true;
437          }
438
439          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
440          store_data(&bfd, compress_buf, compress_len);
441          total += compress_len;
442          fileAddr += compress_len;
443          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
444             compress_len);
445       }
446 #else
447       if (extract) {
448          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
449          extract = false;
450          return true;
451       }
452 #endif
453       break;
454
455    case STREAM_MD5_DIGEST:
456    case STREAM_SHA1_DIGEST:
457    case STREAM_SHA256_DIGEST:
458    case STREAM_SHA512_DIGEST:
459       break;
460
461    case STREAM_SIGNED_DIGEST:
462    case STREAM_ENCRYPTED_SESSION_DATA:
463       // TODO landonf: Investigate crypto support in the storage daemon
464       break;
465
466    case STREAM_PROGRAM_NAMES:
467    case STREAM_PROGRAM_DATA:
468       if (!prog_name_msg) {
469          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
470          prog_name_msg++;
471       }
472       break;
473
474    default:
475       /* If extracting, wierd stream (not 1 or 2), close output file anyway */
476       if (extract) {
477          if (!is_bopen(&bfd)) {
478             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
479          }
480          set_attributes(jcr, attr, &bfd);
481          extract = false;
482       }
483       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
484          rec->Stream);
485       break;
486
487    } /* end switch */
488    return true;
489 }
490
491 /* Dummies to replace askdir.c */
492 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
493 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
494 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
495 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
496 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
497 bool    dir_send_job_status(JCR *jcr) {return 1;}
498
499
500 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
501 {
502    DEVICE *dev = dcr->dev;
503    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
504       dcr->VolumeName, dev->print_name());
505    dev->close();
506    getchar();
507    return true;
508 }
509
510 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
511 {
512    Dmsg0(100, "Fake dir_get_volume_info\n");
513    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
514    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
515    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
516    return 1;
517 }
518