]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
Get correct slot when auto unloading a device.
[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 "       -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 static bool store_data(BFILE *bfd, char *data, const int32_t length)
275 {
276    if (is_win32_stream(attr->data_stream) && !have_win32_api()) {
277       set_portable_backup(bfd);
278       if (!processWin32BackupAPIBlock(bfd, data, length)) {
279          berrno be;
280          Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
281                attr->ofname, be.bstrerror());
282          return false;
283       }
284    } else if (bwrite(bfd, data, length) != (ssize_t)length) {
285       berrno be;
286       Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"),
287             attr->ofname, be.bstrerror());
288       return false;
289    }
290
291    return true;
292 }
293
294 /*
295  * Called here for each record from read_records()
296  */
297 static bool record_cb(DCR *dcr, DEV_RECORD *rec)
298 {
299    int stat;
300    JCR *jcr = dcr->jcr;
301
302    if (rec->FileIndex < 0) {
303       return true;                    /* we don't want labels */
304    }
305
306    /* File Attributes stream */
307
308    switch (rec->Stream) {
309    case STREAM_UNIX_ATTRIBUTES:
310    case STREAM_UNIX_ATTRIBUTES_EX:
311
312       /* If extracting, it was from previous stream, so
313        * close the output file.
314        */
315       if (extract) {
316          if (!is_bopen(&bfd)) {
317             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
318          }
319          set_attributes(jcr, attr, &bfd);
320          extract = false;
321       }
322
323       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
324          Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
325       }
326
327       if (attr->file_index != rec->FileIndex) {
328          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
329             rec->FileIndex, attr->file_index);
330       }
331
332       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
333
334          attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
335          if (!is_restore_stream_supported(attr->data_stream)) {
336             if (!non_support_data++) {
337                Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
338                   stream_to_ascii(attr->data_stream));
339             }
340             extract = false;
341             return true;
342          }
343
344          build_attr_output_fnames(jcr, attr);
345
346          if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
347             Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
348             extract = false;
349             return true;
350          }
351
352          extract = false;
353          stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
354          switch (stat) {
355          case CF_ERROR:
356          case CF_SKIP:
357             break;
358          case CF_EXTRACT:
359             extract = true;
360             print_ls_output(jcr, attr);
361             num_files++;
362             fileAddr = 0;
363             break;
364          case CF_CREATED:
365             set_attributes(jcr, attr, &bfd);
366             print_ls_output(jcr, attr);
367             num_files++;
368             fileAddr = 0;
369             break;
370          }
371       }
372       break;
373
374    /* Data stream and extracting */
375    case STREAM_FILE_DATA:
376    case STREAM_SPARSE_DATA:
377    case STREAM_WIN32_DATA:
378
379       if (extract) {
380          if (rec->Stream == STREAM_SPARSE_DATA) {
381             ser_declare;
382             uint64_t faddr;
383             wbuf = rec->data + SPARSE_FADDR_SIZE;
384             wsize = rec->data_len - SPARSE_FADDR_SIZE;
385             ser_begin(rec->data, SPARSE_FADDR_SIZE);
386             unser_uint64(faddr);
387             if (fileAddr != faddr) {
388                fileAddr = faddr;
389                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
390                   berrno be;
391                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
392                      attr->ofname, be.bstrerror());
393                }
394             }
395          } else {
396             wbuf = rec->data;
397             wsize = rec->data_len;
398          }
399          total += wsize;
400          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
401          store_data(&bfd, wbuf, wsize);
402          fileAddr += wsize;
403       }
404       break;
405
406    /* GZIP data stream */
407    case STREAM_GZIP_DATA:
408    case STREAM_SPARSE_GZIP_DATA:
409    case STREAM_WIN32_GZIP_DATA:
410 #ifdef HAVE_LIBZ
411       if (extract) {
412          uLong compress_len;
413          int stat;
414
415          if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
416             ser_declare;
417             uint64_t faddr;
418             char ec1[50];
419             wbuf = rec->data + SPARSE_FADDR_SIZE;
420             wsize = rec->data_len - SPARSE_FADDR_SIZE;
421             ser_begin(rec->data, SPARSE_FADDR_SIZE);
422             unser_uint64(faddr);
423             if (fileAddr != faddr) {
424                fileAddr = faddr;
425                if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
426                   berrno be;
427                   Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
428                      edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
429                   extract = false;
430                   return true;
431                }
432             }
433          } else {
434             wbuf = rec->data;
435             wsize = rec->data_len;
436          }
437          compress_len = compress_buf_size;
438          if ((stat=uncompress((Bytef *)compress_buf, &compress_len,
439                (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
440             Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), stat);
441             extract = false;
442             return true;
443          }
444
445          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
446          store_data(&bfd, compress_buf, compress_len);
447          total += compress_len;
448          fileAddr += compress_len;
449          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
450             compress_len);
451       }
452 #else
453       if (extract) {
454          Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
455          extract = false;
456          return true;
457       }
458 #endif
459       break;
460
461    case STREAM_MD5_DIGEST:
462    case STREAM_SHA1_DIGEST:
463    case STREAM_SHA256_DIGEST:
464    case STREAM_SHA512_DIGEST:
465       break;
466
467    case STREAM_SIGNED_DIGEST:
468    case STREAM_ENCRYPTED_SESSION_DATA:
469       // TODO landonf: Investigate crypto support in the storage daemon
470       break;
471
472    case STREAM_PROGRAM_NAMES:
473    case STREAM_PROGRAM_DATA:
474       if (!prog_name_msg) {
475          Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
476          prog_name_msg++;
477       }
478       break;
479
480    default:
481       /* If extracting, wierd stream (not 1 or 2), close output file anyway */
482       if (extract) {
483          if (!is_bopen(&bfd)) {
484             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
485          }
486          set_attributes(jcr, attr, &bfd);
487          extract = false;
488       }
489       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
490          rec->Stream);
491       break;
492
493    } /* end switch */
494    return true;
495 }
496
497 /* Dummies to replace askdir.c */
498 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
499 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
500 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
501 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
502 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
503 bool    dir_send_job_status(JCR *jcr) {return 1;}
504
505
506 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
507 {
508    DEVICE *dev = dcr->dev;
509    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
510       dcr->VolumeName, dev->print_name());
511    dev->close();
512    getchar();
513    return true;
514 }
515
516 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
517 {
518    Dmsg0(100, "Fake dir_get_volume_info\n");
519    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
520    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
521    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
522    return 1;
523 }
524