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