]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
kes Suppress chown and chmod error messages if the FD is not running
[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 "       -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.bstrerror());
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.bstrerror());
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
224    enable_backup_privileges(NULL, 1);
225
226    jcr = setup_jcr("bextract", devname, bsr, VolumeName, 1); /* acquire for read */
227    if (!jcr) {
228       exit(1);
229    }
230    dev = jcr->read_dcr->dev;
231    if (!dev) {
232       exit(1);
233    }
234    dcr = jcr->read_dcr;
235
236    /* Make sure where directory exists and that it is a directory */
237    if (stat(where, &statp) < 0) {
238       berrno be;
239       Emsg2(M_ERROR_TERM, 0, _("Cannot stat %s. It must exist. ERR=%s\n"),
240          where, be.bstrerror());
241    }
242    if (!S_ISDIR(statp.st_mode)) {
243       Emsg1(M_ERROR_TERM, 0, _("%s must be a directory.\n"), where);
244    }
245
246    free(jcr->where);
247    jcr->where = bstrdup(where);
248    attr = new_attr(jcr);
249
250    compress_buf = get_memory(compress_buf_size);
251
252    read_records(dcr, record_cb, mount_next_read_volume);
253    /* If output file is still open, it was the last one in the
254     * archive since we just hit an end of file, so close the file.
255     */
256    if (is_bopen(&bfd)) {
257       set_attributes(jcr, attr, &bfd);
258    }
259    release_device(dcr);
260    free_attr(attr);
261    free_jcr(jcr);
262    dev->term();
263
264    printf(_("%u files restored.\n"), num_files);
265    return;
266 }
267
268 /*
269  * Called here for each record from read_records()
270  */
271 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
272 {
273    int stat;
274    JCR *jcr = dcr->jcr;
275
276    if (rec->FileIndex < 0) {
277       return true;                    /* we don't want labels */
278    }
279
280    /* File Attributes stream */
281
282    switch (rec->Stream) {
283    case STREAM_UNIX_ATTRIBUTES:
284    case STREAM_UNIX_ATTRIBUTES_EX:
285
286       /* If extracting, it was from previous stream, so
287        * close the output file.
288        */
289       if (extract) {
290          if (!is_bopen(&bfd)) {
291             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
292          }
293          set_attributes(jcr, attr, &bfd);
294          extract = false;
295       }
296
297       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
298          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
299       }
300
301       if (attr->file_index != rec->FileIndex) {
302          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
303             rec->FileIndex, attr->file_index);
304       }
305
306       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
307
308          attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
309          if (!is_restore_stream_supported(attr->data_stream)) {
310             if (!non_support_data++) {
311                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
312                   stream_to_ascii(attr->data_stream));
313             }
314             extract = false;
315             return true;
316          }
317
318
319          build_attr_output_fnames(jcr, attr);
320
321          extract = false;
322          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
323          switch (stat) {
324          case CF_ERROR:
325          case CF_SKIP:
326             break;
327          case CF_EXTRACT:
328             extract = true;
329             print_ls_output(jcr, attr);
330             num_files++;
331             fileAddr = 0;
332             break;
333          case CF_CREATED:
334             set_attributes(jcr, attr, &bfd);
335             print_ls_output(jcr, attr);
336             num_files++;
337             fileAddr = 0;
338             break;
339          }
340       }
341       break;
342
343    /* Data stream and extracting */
344    case STREAM_FILE_DATA:
345    case STREAM_SPARSE_DATA:
346    case STREAM_WIN32_DATA:
347
348       if (extract) {
349          if (rec->Stream == STREAM_SPARSE_DATA) {
350             ser_declare;
351             uint64_t faddr;
352             wbuf = rec->data + SPARSE_FADDR_SIZE;
353             wsize = rec->data_len - SPARSE_FADDR_SIZE;
354             ser_begin(rec->data, SPARSE_FADDR_SIZE);
355             unser_uint64(faddr);
356             if (fileAddr != faddr) {
357                fileAddr = faddr;
358                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
359                   berrno be;
360                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
361                      attr->ofname, be.bstrerror());
362                }
363             }
364          } else {
365             wbuf = rec->data;
366             wsize = rec->data_len;
367          }
368          total += wsize;
369          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
370          if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
371             berrno be;
372             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
373                attr->ofname, be.bstrerror());
374          }
375          fileAddr += wsize;
376       }
377       break;
378
379    /* GZIP data stream */
380    case STREAM_GZIP_DATA:
381    case STREAM_SPARSE_GZIP_DATA:
382    case STREAM_WIN32_GZIP_DATA:
383 #ifdef HAVE_LIBZ
384       if (extract) {
385          uLong compress_len;
386          int stat;
387
388          if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
389             ser_declare;
390             uint64_t faddr;
391             char ec1[50];
392             wbuf = rec->data + SPARSE_FADDR_SIZE;
393             wsize = rec->data_len - SPARSE_FADDR_SIZE;
394             ser_begin(rec->data, SPARSE_FADDR_SIZE);
395             unser_uint64(faddr);
396             if (fileAddr != faddr) {
397                fileAddr = faddr;
398                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
399                   berrno be;
400                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
401                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
402                   extract = false;
403                   return true;
404                }
405             }
406          } else {
407             wbuf = rec->data;
408             wsize = rec->data_len;
409          }
410          compress_len = compress_buf_size;
411          if ((stat=uncompress((Bytef *)compress_buf, &compress_len,
412                (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
413             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
414             extract = false;
415             return true;
416          }
417
418          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
419          if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
420             berrno be;
421             Pmsg0(0, _("===Write error===\n"));
422             Emsg2(M_ERROR, 0, _("Write error on %s: %s\n"),
423                attr->ofname, be.bstrerror());
424             extract = false;
425             return true;
426          }
427          total += compress_len;
428          fileAddr += compress_len;
429          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
430             compress_len);
431       }
432 #else
433       if (extract) {
434          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
435          extract = false;
436          return true;
437       }
438 #endif
439       break;
440
441    case STREAM_MD5_DIGEST:
442    case STREAM_SHA1_DIGEST:
443    case STREAM_SHA256_DIGEST:
444    case STREAM_SHA512_DIGEST:
445       break;
446
447    case STREAM_SIGNED_DIGEST:
448    case STREAM_ENCRYPTED_SESSION_DATA:
449       // TODO landonf: Investigate crypto support in the storage daemon
450       break;
451
452    case STREAM_PROGRAM_NAMES:
453    case STREAM_PROGRAM_DATA:
454       if (!prog_name_msg) {
455          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
456          prog_name_msg++;
457       }
458       break;
459
460    default:
461       /* If extracting, wierd stream (not 1 or 2), close output file anyway */
462       if (extract) {
463          if (!is_bopen(&bfd)) {
464             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
465          }
466          set_attributes(jcr, attr, &bfd);
467          extract = false;
468       }
469       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
470          rec->Stream);
471       break;
472
473    } /* end switch */
474    return true;
475 }
476
477 /* Dummies to replace askdir.c */
478 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
479 bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
480 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
481 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
482 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
483 bool    dir_send_job_status(JCR *jcr) {return 1;}
484
485
486 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
487 {
488    DEVICE *dev = dcr->dev;
489    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
490       dcr->VolumeName, dev->print_name());
491    dev->close();
492    getchar();
493    return true;
494 }
495
496 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
497 {
498    Dmsg0(100, "Fake dir_get_volume_info\n");
499    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
500    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
501    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
502    return 1;
503 }