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