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