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