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