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