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