]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
Fix for reused thread ids by FreeBSD + qfill command
[bacula/bacula] / bacula / src / stored / bextract.c
1 /*
2  *
3  *  Dumb program to extract files from a Bacula backup.
4  *
5  *   Kern E. Sibbald
6  *
7  *   Version $Id$
8  *
9  */
10 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "stored.h"
32 #include "findlib/find.h"
33
34 #ifdef HAVE_CYGWIN
35 int win32_client = 1;
36 #else
37 int win32_client = 0;
38 #endif
39
40
41 static void do_extract(char *fname);
42 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
43
44 static DEVICE *dev = NULL;
45 static BFILE bfd;
46 static JCR *jcr;
47 static FF_PKT my_ff;
48 static FF_PKT *ff = &my_ff;
49 static BSR *bsr = NULL;
50 static int extract = FALSE;
51 static long record_file_index;
52 static long total = 0;
53 static POOLMEM *fname;                    /* original file name */
54 static POOLMEM *ofile;                    /* output name with prefix */
55 static POOLMEM *lname;                    /* link name */
56 static POOLMEM *attribsEx;                /* extended attributes (Win32) */
57 static char *where;
58 static int wherelen;                      /* prefix length */
59 static uint32_t num_files = 0;
60 static struct stat statp;
61 static uint32_t compress_buf_size = 70000;
62 static POOLMEM *compress_buf;
63 static int type;
64 static int stream;
65 static int prog_name_msg = 0;
66 static int win32_data_msg = 0;
67 static char *VolumeName = NULL;
68
69 static char *wbuf;                    /* write buffer address */
70 static uint32_t wsize;                /* write size */
71 static uint64_t fileAddr = 0;         /* file write address */
72
73 #define CONFIG_FILE "bacula-sd.conf"
74 char *configfile;
75
76
77 static void usage()
78 {
79    fprintf(stderr,
80 "\nVersion: " VERSION " (" BDATE ")\n\n"
81 "Usage: bextract [-d debug_level] <bacula-archive> <directory-to-store-files>\n"
82 "       -b <file>       specify a bootstrap file\n"
83 "       -c <file>       specify a configuration file\n"
84 "       -dnn            set debug level to nn\n"
85 "       -e <file>       exclude list\n"
86 "       -i <file>       include list\n"
87 "       -V              specify Volume names (separated by |)\n"
88 "       -?              print this message\n\n");
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    int got_inc = FALSE;
99
100    working_directory = "/tmp";
101    my_name_is(argc, argv, "bextract");
102    init_msg(NULL, NULL);              /* setup message handler */
103
104    memset(ff, 0, sizeof(FF_PKT));
105    init_include_exclude_files(ff);
106    binit(&bfd);
107
108    while ((ch = getopt(argc, argv, "b:c:d:e:i:?")) != -1) {
109       switch (ch) {
110          case 'b':                    /* bootstrap file */
111             bsr = parse_bsr(NULL, optarg);
112 //          dump_bsr(bsr);
113             break;
114
115          case 'c':                    /* specify config file */
116             if (configfile != NULL) {
117                free(configfile);
118             }
119             configfile = bstrdup(optarg);
120             break;
121
122          case 'd':                    /* debug level */
123             debug_level = atoi(optarg);
124             if (debug_level <= 0)
125                debug_level = 1; 
126             break;
127
128          case 'e':                    /* exclude list */
129             if ((fd = fopen(optarg, "r")) == NULL) {
130                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
131                   optarg, strerror(errno));
132                exit(1);
133             }
134             while (fgets(line, sizeof(line), fd) != NULL) {
135                strip_trailing_junk(line);
136                Dmsg1(900, "add_exclude %s\n", line);
137                add_fname_to_exclude_list(ff, line);
138             }
139             fclose(fd);
140             break;
141
142          case 'i':                    /* include list */
143             if ((fd = fopen(optarg, "r")) == NULL) {
144                Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
145                   optarg, strerror(errno));
146                exit(1);
147             }
148             while (fgets(line, sizeof(line), fd) != NULL) {
149                strip_trailing_junk(line);
150                Dmsg1(900, "add_include %s\n", line);
151                add_fname_to_include_list(ff, 0, line);
152             }
153             fclose(fd);
154             got_inc = TRUE;
155             break;
156
157          case 'V':                    /* Volume name */
158             VolumeName = optarg;
159             break;
160
161          case '?':
162          default:
163             usage();
164
165       }  
166    }
167    argc -= optind;
168    argv += optind;
169
170    if (argc != 2) {
171       Pmsg0(0, "Wrong number of arguments: \n");
172       usage();
173    }
174
175    if (configfile == NULL) {
176       configfile = bstrdup(CONFIG_FILE);
177    }
178
179    parse_config(configfile);
180
181    if (!got_inc) {                            /* If no include file, */
182       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
183    }
184
185    where = argv[1];
186    do_extract(argv[0]);
187
188    if (bsr) {
189       free_bsr(bsr);
190    }
191    if (prog_name_msg) {
192       Pmsg1(000, "%d Program Name and/or Program Data Stream records ignored.\n",
193          prog_name_msg);
194    }
195    if (win32_data_msg) {
196       Pmsg1(000, "%d Win32 data or Win32 gzip data stream records. Ignored.\n",
197          win32_data_msg);
198    }
199    return 0;
200 }
201
202 static void do_extract(char *devname)
203 {
204
205    jcr = setup_jcr("bextract", devname, bsr, VolumeName);
206    dev = setup_to_access_device(jcr, 1);    /* acquire for read */
207    if (!dev) {
208       exit(1);
209    }
210
211    /* Make sure where directory exists and that it is a directory */
212    if (stat(where, &statp) < 0) {
213       Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n",
214          where, strerror(errno));
215    }
216    if (!S_ISDIR(statp.st_mode)) {
217       Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where);
218    }
219
220    wherelen = strlen(where);
221    fname = get_pool_memory(PM_FNAME);
222    ofile = get_pool_memory(PM_FNAME);
223    lname = get_pool_memory(PM_FNAME);
224    attribsEx = get_pool_memory(PM_FNAME);
225
226    compress_buf = get_memory(compress_buf_size);
227
228    read_records(jcr, dev, record_cb, mount_next_read_volume);
229    /* If output file is still open, it was the last one in the
230     * archive since we just hit an end of file, so close the file. 
231     */
232    if (is_bopen(&bfd)) {
233       set_attributes(jcr, fname, ofile, lname, type, stream, &statp,
234                      attribsEx, &bfd);
235    }
236    release_device(jcr, dev);
237
238    free_pool_memory(fname);
239    free_pool_memory(ofile);
240    free_pool_memory(lname);
241    free_pool_memory(compress_buf);
242    term_dev(dev);
243    free_jcr(jcr);
244    printf("%u files restored.\n", num_files);
245    return;
246 }
247
248 /*
249  * Called here for each record from read_records()
250  */
251 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
252 {
253    int stat;
254
255    if (rec->FileIndex < 0) {
256       return;                         /* we don't want labels */
257    }
258
259    /* File Attributes stream */
260    if (rec->Stream == STREAM_UNIX_ATTRIBUTES || rec->Stream == STREAM_WIN32_ATTRIBUTES) {
261       char *ap, *lp, *fp, *apex;
262
263       stream = rec->Stream;
264
265       /* If extracting, it was from previous stream, so
266        * close the output file.
267        */
268       if (extract) {
269          if (!is_bopen(&bfd)) {
270             Emsg0(M_ERROR, 0, "Logic error output file should be open but is not.\n");
271          }
272          extract = FALSE;
273          set_attributes(jcr, fname, ofile, lname, type, stream, &statp,
274                         attribsEx, &bfd);
275       }
276
277       if (sizeof_pool_memory(fname) < rec->data_len) {
278          fname = realloc_pool_memory(fname, rec->data_len + 1);
279       }
280       if (sizeof_pool_memory(ofile) < rec->data_len + wherelen + 1) {
281          ofile = realloc_pool_memory(ofile, rec->data_len + wherelen + 1);
282       }
283       if (sizeof_pool_memory(lname) < rec->data_len) {
284          lname = realloc_pool_memory(lname, rec->data_len + wherelen + 1);
285       }
286       *fname = 0;
287       *lname = 0;
288
289       /*              
290        * An Attributes record consists of:
291        *    File_index
292        *    Type   (FT_types)
293        *    Filename
294        *    Attributes
295        *    Link name (if file linked i.e. FT_LNK)
296        *    Extended Attributes (Win32) 
297        *
298        */
299       sscanf(rec->data, "%ld %d", &record_file_index, &type);
300       if (record_file_index != rec->FileIndex)
301          Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
302             rec->FileIndex, record_file_index);
303       ap = rec->data;
304       while (*ap++ != ' ')         /* skip record file index */
305          ;
306       while (*ap++ != ' ')         /* skip type */
307          ;
308       /* Save filename and position to attributes */
309       fp = fname;
310       while (*ap != 0) {
311          *fp++  = *ap++;
312       }
313       *fp = *ap++;                 /* terminate filename & point to attribs */
314
315       /* Skip to Link name */
316       if (type == FT_LNK || type == FT_LNKSAVED) {
317          lp = ap;
318          while (*lp++ != 0) {
319             ;
320          }
321       } else {
322          lp = "";
323       }
324
325       if (rec->Stream == STREAM_WIN32_ATTRIBUTES) {
326          apex = ap;                   /* start at attributes */
327          while (*apex++ != 0) {       /* skip attributes */
328             ;
329          }
330          while (*apex++ != 0) {      /* skip link name */
331             ;
332          }
333          pm_strcpy(&attribsEx, apex);  /* make a copy of Extended attributes */
334       } else {
335          *attribsEx = 0;              /* no extended attributes */
336       }
337
338          
339       if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
340          uint32_t LinkFI;
341
342          decode_stat(ap, &statp, &LinkFI);
343          /*
344           * Prepend the where directory so that the
345           * files are put where the user wants.
346           *
347           * We do a little jig here to handle Win32 files with
348           *   a drive letter -- we simply strip the drive: from
349           *   every filename if a prefix is supplied.
350           */
351          if (where[0] == 0) {
352             strcpy(ofile, fname);
353             strcpy(lname, lp);
354          } else {
355             char *fn;
356             strcpy(ofile, where);
357             if (win32_client && fname[1] == ':') {
358                fn = fname+2;          /* skip over drive: */
359             } else {
360                fn = fname;            /* take whole name */
361             }
362             /* Ensure where is terminated with a slash */
363             if (where[wherelen-1] != '/' && fn[0] != '/') {
364                strcat(ofile, "/");
365             }
366             strcat(ofile, fn);        /* copy rest of name */
367             /* Fixup link name for hard links, but not for
368              * soft links 
369              */
370             if (type == FT_LNKSAVED) {
371                if (lp[0] == '/') {      /* if absolute path */
372                   strcpy(lname, where);
373                }       
374                if (win32_client && lp[1] == ':') {
375                   strcat(lname, lp+2); /* copy rest of name */
376                } else {
377                   strcat(lname, lp);   /* On Unix systems we take everything */
378                }
379             }
380          }
381
382          /*          Pmsg1(000, "Restoring: %s\n", ofile); */
383
384          extract = FALSE;
385          stat = create_file(jcr, fname, ofile, lname, type, stream,
386                             &statp, attribsEx, &bfd, REPLACE_ALWAYS);   
387          switch (stat) {
388          case CF_ERROR:
389          case CF_SKIP:
390             break;
391          case CF_EXTRACT:
392             extract = TRUE;
393             print_ls_output(ofile, lname, type, &statp);   
394             num_files++;
395             fileAddr = 0;
396             break;
397          case CF_CREATED:
398             set_attributes(jcr, fname, ofile, lname, type, stream, &statp,
399                            attribsEx, &bfd);
400             print_ls_output(ofile, lname, type, &statp);   
401             num_files++;
402             fileAddr = 0;
403             break;
404          }  
405       }
406
407    /* Data stream and extracting */
408    } else if (rec->Stream == STREAM_FILE_DATA || rec->Stream == STREAM_SPARSE_DATA) {
409       if (extract) {
410          if (rec->Stream == STREAM_SPARSE_DATA) {
411             ser_declare;
412             uint64_t faddr;
413             wbuf = rec->data + SPARSE_FADDR_SIZE;
414             wsize = rec->data_len - SPARSE_FADDR_SIZE;
415             ser_begin(rec->data, SPARSE_FADDR_SIZE);
416             unser_uint64(faddr);
417             if (fileAddr != faddr) {
418                fileAddr = faddr;
419                if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
420                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), ofile, strerror(errno));
421                }
422             }
423          } else {
424             wbuf = rec->data;
425             wsize = rec->data_len;
426          }
427          total += wsize;
428          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
429          if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
430             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), ofile, strerror(errno));
431          }
432          fileAddr += wsize;
433       }
434
435    } else if (rec->Stream == STREAM_GZIP_DATA || rec->Stream == STREAM_SPARSE_GZIP_DATA) {
436 #ifdef HAVE_LIBZ
437       if (extract) {
438          uLongf compress_len;
439          int stat;
440
441          if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
442             ser_declare;
443             uint64_t faddr;
444             wbuf = rec->data + SPARSE_FADDR_SIZE;
445             wsize = rec->data_len - SPARSE_FADDR_SIZE;
446             ser_begin(rec->data, SPARSE_FADDR_SIZE);
447             unser_uint64(faddr);
448             if (fileAddr != faddr) {
449                fileAddr = faddr;
450                if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
451                   Emsg2(M_ERROR, 0, _("Seek error on %s: %s\n"), ofile, strerror(errno));
452                }
453             }
454          } else {
455             wbuf = rec->data;
456             wsize = rec->data_len;
457          }
458          compress_len = compress_buf_size;
459          if ((stat=uncompress((Bytef *)compress_buf, &compress_len, 
460                (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
461             Emsg1(M_ERROR_TERM, 0, _("Uncompression error. ERR=%d\n"), stat);
462          }
463
464          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
465          if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
466             Pmsg0(0, "===Write error===\n");
467             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), ofile, strerror(errno));
468          }
469          total += compress_len;
470          fileAddr += compress_len;
471          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
472             compress_len);
473       }
474 #else
475       if (extract) {
476          Emsg0(M_ERROR, 0, "GZIP data stream found, but GZIP not configured!\n");
477       }
478 #endif
479
480
481    /* If extracting, wierd stream (not 1 or 2), close output file anyway */
482    } else 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       extract = FALSE;
487       set_attributes(jcr, fname, ofile, lname, type, stream, &statp,
488                      attribsEx, &bfd);
489    } else if (rec->Stream == STREAM_PROGRAM_NAMES || rec->Stream == STREAM_PROGRAM_DATA) {
490       if (!prog_name_msg) {
491          Pmsg0(000, "Got Program Name or Data Stream. Ignored.\n");
492          prog_name_msg++;
493       }
494    } else if (rec->Stream == STREAM_WIN32_DATA || rec->Stream == STREAM_WIN32_GZIP_DATA) {
495       if (!win32_data_msg) {
496          Pmsg0(000, "Got Win32 data or Win32 gzip data stream. Ignored.\n");
497          win32_data_msg++;
498       }
499    } else if (!(rec->Stream == STREAM_MD5_SIGNATURE ||
500                 rec->Stream == STREAM_SHA1_SIGNATURE)) {
501       Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
502    }
503 }
504
505
506
507
508 /* Dummies to replace askdir.c */
509 int     dir_get_volume_info(JCR *jcr, int writing) { return 1;}
510 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
511 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
512 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
513 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
514 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
515 int     dir_send_job_status(JCR *jcr) {return 1;}
516
517
518 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
519 {
520    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
521       jcr->VolumeName, dev_name(dev));
522    getchar();   
523    return 1;
524 }