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