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