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