]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
9b6c1213b21be3c466951c0a5dc40f7a6c5a5a2f
[bacula/bacula] / bacula / src / stored / bextract.c
1 /*
2  *
3  *  Dumb program to extract files from a Bacula backup.
4  *
5  *   Kern E. Sibbald, MM
6  *
7  *   Version $Id$
8  *
9  */
10 /*
11    Copyright (C) 2000-2006 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"
26 #include "stored.h"
27 #include "findlib/find.h"
28
29 static void do_extract(char *fname);
30 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
31
32 static DEVICE *dev = NULL;
33 static DCR *dcr;
34 static BFILE bfd;
35 static JCR *jcr;
36 static FF_PKT *ff;
37 static BSR *bsr = NULL;
38 static bool extract = false;
39 static int non_support_data = 0;
40 static long total = 0;
41 static ATTR *attr;
42 static char *where;
43 static uint32_t num_files = 0;
44 static uint32_t compress_buf_size = 70000;
45 static POOLMEM *compress_buf;
46 static int prog_name_msg = 0;
47 static int win32_data_msg = 0;
48 static char *VolumeName = NULL;
49
50 static char *wbuf;                    /* write buffer address */
51 static uint32_t wsize;                /* write size */
52 static uint64_t fileAddr = 0;         /* file write address */
53
54 #define CONFIG_FILE "bacula-sd.conf"
55 char *configfile = NULL;
56 STORES *me = NULL;                    /* our Global resource */
57 bool forge_on = false;
58 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
59 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
60
61 static void usage()
62 {
63    fprintf(stderr, _(
64 "Copyright (C) 2000-%s Kern Sibbald.\n"
65 "\nVersion: %s (%s)\n\n"
66 "Usage: bextract <options> <bacula-archive-device-name> <directory-to-store-files>\n"
67 "       -b <file>       specify a bootstrap file\n"
68 "       -c <file>       specify a configuration file\n"
69 "       -d <nn>         set debug level to nn\n"
70 "       -e <file>       exclude list\n"
71 "       -i <file>       include list\n"
72 "       -p              proceed inspite of I/O errors\n"
73 "       -v              verbose\n"
74 "       -V <volumes>    specify Volume names (separated by |)\n"
75 "       -?              print this message\n\n"), BYEAR, VERSION, BDATE);
76    exit(1);
77 }
78
79
80 int main (int argc, char *argv[])
81 {
82    int ch;
83    FILE *fd;
84    char line[1000];
85    bool got_inc = false;
86
87    setlocale(LC_ALL, "");
88    bindtextdomain("bacula", LOCALEDIR);
89    textdomain("bacula");
90    init_stack_dump();
91
92    working_directory = "/tmp";
93    my_name_is(argc, argv, "bextract");
94    init_msg(NULL, NULL);              /* setup message handler */
95
96    OSDependentInit();
97
98    ff = init_find_files();
99    binit(&bfd);
100
101    while ((ch = getopt(argc, argv, "b:c:d:e:i:pvV:?")) != -1) {
102       switch (ch) {
103       case 'b':                    /* bootstrap file */
104          bsr = parse_bsr(NULL, optarg);
105 //       dump_bsr(bsr, true);
106          break;
107
108       case 'c':                    /* specify config file */
109          if (configfile != NULL) {
110             free(configfile);
111          }
112          configfile = bstrdup(optarg);
113          break;
114
115       case 'd':                    /* debug level */
116          debug_level = atoi(optarg);
117          if (debug_level <= 0)
118             debug_level = 1;
119          break;
120
121       case 'e':                    /* exclude list */
122          if ((fd = fopen(optarg, "rb")) == NULL) {
123             berrno be;
124             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
125                optarg, be.strerror());
126             exit(1);
127          }
128          while (fgets(line, sizeof(line), fd) != NULL) {
129             strip_trailing_junk(line);
130             Dmsg1(900, "add_exclude %s\n", line);
131             add_fname_to_exclude_list(ff, line);
132          }
133          fclose(fd);
134          break;
135
136       case 'i':                    /* include list */
137          if ((fd = fopen(optarg, "rb")) == NULL) {
138             berrno be;
139             Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
140                optarg, be.strerror());
141             exit(1);
142          }
143          while (fgets(line, sizeof(line), fd) != NULL) {
144             strip_trailing_junk(line);
145             Dmsg1(900, "add_include %s\n", line);
146             add_fname_to_include_list(ff, 0, line);
147          }
148          fclose(fd);
149          got_inc = true;
150          break;
151
152       case 'p':
153          forge_on = true;
154          break;
155
156       case 'v':
157          verbose++;
158          break;
159
160       case 'V':                    /* Volume name */
161          VolumeName = optarg;
162          break;
163
164       case '?':
165       default:
166          usage();
167
168       } /* end switch */
169    } /* end while */
170    argc -= optind;
171    argv += optind;
172
173    if (argc != 2) {
174       Pmsg0(0, _("Wrong number of arguments: \n"));
175       usage();
176    }
177
178    if (configfile == NULL) {
179       configfile = bstrdup(CONFIG_FILE);
180    }
181
182    parse_config(configfile);
183
184    if (!got_inc) {                            /* If no include file, */
185       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
186    }
187
188    where = argv[1];
189    do_extract(argv[0]);
190
191    if (bsr) {
192       free_bsr(bsr);
193    }
194    if (prog_name_msg) {
195       Pmsg1(000, _("%d Program Name and/or Program Data Stream records ignored.\n"),
196          prog_name_msg);
197    }
198    if (win32_data_msg) {
199       Pmsg1(000, _("%d Win32 data or Win32 gzip data stream records. Ignored.\n"),
200          win32_data_msg);
201    }
202    term_include_exclude_files(ff);
203    term_find_files(ff);
204    return 0;
205 }
206
207 static void do_extract(char *devname)
208 {
209    struct stat statp;
210    jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
211    if (!jcr) {
212       exit(1);
213    }
214    dev = jcr->read_dcr->dev;
215    if (!dev) {
216       exit(1);
217    }
218    dcr = jcr->read_dcr;
219
220    /* Make sure where directory exists and that it is a directory */
221    if (stat(where, &statp) < 0) {
222       berrno be;
223       Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
224          where, be.strerror());
225    }
226    if (!S_ISDIR(statp.st_mode)) {
227       Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
228    }
229
230    free(jcr->where);
231    jcr->where = bstrdup(where);
232    attr = new_attr();
233
234    compress_buf = get_memory(compress_buf_size);
235
236    read_records(dcr, record_cb, mount_next_read_volume);
237    /* If output file is still open, it was the last one in the
238     * archive since we just hit an end of file, so close the file.
239     */
240    if (is_bopen(&bfd)) {
241       set_attributes(jcr, attr, &bfd);
242    }
243    release_device(dcr);
244    free_attr(attr);
245    free_jcr(jcr);
246    dev->term();
247
248    printf(_("%u files restored.\n"), num_files);
249    return;
250 }
251
252 /*
253  * Called here for each record from read_records()
254  */
255 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
256 {
257    int stat;
258    JCR *jcr = dcr->jcr;
259
260    if (rec->FileIndex < 0) {
261       return true;                    /* we don't want labels */
262    }
263
264    /* File Attributes stream */
265
266    switch (rec->Stream) {
267    case STREAM_UNIX_ATTRIBUTES:
268    case STREAM_UNIX_ATTRIBUTES_EX:
269
270       /* If extracting, it was from previous stream, so
271        * close the output file.
272        */
273       if (extract) {
274          if (!is_bopen(&bfd)) {
275             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
276          }
277          set_attributes(jcr, attr, &bfd);
278          extract = false;
279       }
280
281       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
282          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
283       }
284
285       if (attr->file_index != rec->FileIndex) {
286          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
287             rec->FileIndex, attr->file_index);
288       }
289
290       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
291
292          attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
293          if (!is_restore_stream_supported(attr->data_stream)) {
294             if (!non_support_data++) {
295                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
296                   stream_to_ascii(attr->data_stream));
297             }
298             extract = false;
299             return true;
300          }
301
302
303          build_attr_output_fnames(jcr, attr);
304
305          extract = false;
306          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
307          switch (stat) {
308          case CF_ERROR:
309          case CF_SKIP:
310             break;
311          case CF_EXTRACT:
312             extract = true;
313             print_ls_output(jcr, attr);
314             num_files++;
315             fileAddr = 0;
316             break;
317          case CF_CREATED:
318             set_attributes(jcr, attr, &bfd);
319             print_ls_output(jcr, attr);
320             num_files++;
321             fileAddr = 0;
322             break;
323          }
324       }
325       break;
326
327    /* Data stream and extracting */
328    case STREAM_FILE_DATA:
329    case STREAM_SPARSE_DATA:
330    case STREAM_WIN32_DATA:
331
332       if (extract) {
333          if (rec->Stream == STREAM_SPARSE_DATA) {
334             ser_declare;
335             uint64_t faddr;
336             wbuf = rec->data + SPARSE_FADDR_SIZE;
337             wsize = rec->data_len - SPARSE_FADDR_SIZE;
338             ser_begin(rec->data, SPARSE_FADDR_SIZE);
339             unser_uint64(faddr);
340             if (fileAddr != faddr) {
341                fileAddr = faddr;
342                if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
343                   berrno be;
344                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
345                      attr->ofname, be.strerror());
346                }
347             }
348          } else {
349             wbuf = rec->data;
350             wsize = rec->data_len;
351          }
352          total += wsize;
353          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
354          if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
355             berrno be;
356             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
357                attr->ofname, be.strerror());
358          }
359          fileAddr += wsize;
360       }
361       break;
362
363    /* GZIP data stream */
364    case STREAM_GZIP_DATA:
365    case STREAM_SPARSE_GZIP_DATA:
366    case STREAM_WIN32_GZIP_DATA:
367 #ifdef HAVE_LIBZ
368       if (extract) {
369          uLong compress_len;
370          int stat;
371
372          if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
373             ser_declare;
374             uint64_t faddr;
375             char ec1[50];
376             wbuf = rec->data + SPARSE_FADDR_SIZE;
377             wsize = rec->data_len - SPARSE_FADDR_SIZE;
378             ser_begin(rec->data, SPARSE_FADDR_SIZE);
379             unser_uint64(faddr);
380             if (fileAddr != faddr) {
381                fileAddr = faddr;
382                if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
383                   berrno be;
384                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
385                      edit_uint64(fileAddr, ec1), attr->ofname, be.strerror());
386                   extract = false;
387                   return true;
388                }
389             }
390          } else {
391             wbuf = rec->data;
392             wsize = rec->data_len;
393          }
394          compress_len = compress_buf_size;
395          if ((stat=uncompress((Bytef *)compress_buf, &compress_len,
396                (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
397             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
398             extract = false;
399             return true;
400          }
401
402          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
403          if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
404             berrno be;
405             Pmsg0(0, _("===Write error===\n"));
406             Emsg2(M_ERROR, 0, _("Write error on %s: %s\n"),
407                attr->ofname, be.strerror());
408             extract = false;
409             return true;
410          }
411          total += compress_len;
412          fileAddr += compress_len;
413          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
414             compress_len);
415       }
416 #else
417       if (extract) {
418          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
419          extract = false;
420          return true;
421       }
422 #endif
423       break;
424
425    case STREAM_MD5_DIGEST:
426    case STREAM_SHA1_DIGEST:
427    case STREAM_SHA256_DIGEST:
428    case STREAM_SHA512_DIGEST:
429       break;
430
431    case STREAM_SIGNED_DIGEST:
432    case STREAM_ENCRYPTED_SESSION_DATA:
433       // TODO landonf: Investigate crypto support in the storage daemon
434       break;
435
436    case STREAM_PROGRAM_NAMES:
437    case STREAM_PROGRAM_DATA:
438       if (!prog_name_msg) {
439          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
440          prog_name_msg++;
441       }
442       break;
443
444    default:
445       /* If extracting, wierd stream (not 1 or 2), close output file anyway */
446       if (extract) {
447          if (!is_bopen(&bfd)) {
448             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
449          }
450          set_attributes(jcr, attr, &bfd);
451          extract = false;
452       }
453       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
454          rec->Stream);
455       break;
456
457    } /* end switch */
458    return true;
459 }
460
461 /* Dummies to replace askdir.c */
462 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
463 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
464 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
465 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
466 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
467 bool    dir_send_job_status(JCR *jcr) {return 1;}
468
469
470 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
471 {
472    DEVICE *dev = dcr->dev;
473    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
474       dcr->VolumeName, dev->print_name());
475    dev->close();
476    getchar();
477    return true;
478 }
479
480 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
481 {
482    Dmsg0(100, "Fake dir_get_volume_info\n");
483    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
484    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
485    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
486    return 1;
487 }