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