]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
Autochanger for read + 64 bit addrs + Session key, see kes14Sep02
[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 int ofd = -1;
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 char *where;
57 static int wherelen;                      /* prefix length */
58 static uint32_t num_files = 0;
59 static struct stat statp;
60 static uint32_t compress_buf_size = 70000;
61 static POOLMEM *compress_buf;
62 static int type;
63
64 static void usage()
65 {
66    fprintf(stderr,
67 "\nVersion: " VERSION " (" DATE ")\n\n"
68 "Usage: bextract [-d debug_level] <bacula-archive> <directory-to-store-files>\n"
69 "       -b <file>       specify a bootstrap file\n"
70 "       -dnn            set debug level to nn\n"
71 "       -e <file>       exclude list\n"
72 "       -i <file>       include list\n"
73 "       -?              print this message\n\n");
74    exit(1);
75 }
76
77
78 int main (int argc, char *argv[])
79 {
80    int ch;   
81    FILE *fd;
82    char line[1000];
83    int got_inc = FALSE;
84
85    my_name_is(argc, argv, "bextract");
86    init_msg(NULL, NULL);              /* setup message handler */
87
88    memset(ff, 0, sizeof(FF_PKT));
89    init_include_exclude_files(ff);
90
91    while ((ch = getopt(argc, argv, "b:d:e:i:?")) != -1) {
92       switch (ch) {
93          case 'b':                    /* bootstrap file */
94             bsr = parse_bsr(NULL, optarg);
95 //          dump_bsr(bsr);
96             break;
97
98          case 'd':                    /* debug level */
99             debug_level = atoi(optarg);
100             if (debug_level <= 0)
101                debug_level = 1; 
102             break;
103
104          case 'e':                    /* exclude list */
105             if ((fd = fopen(optarg, "r")) == NULL) {
106                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
107                   optarg, strerror(errno));
108                exit(1);
109             }
110             while (fgets(line, sizeof(line), fd) != NULL) {
111                strip_trailing_junk(line);
112                Dmsg1(900, "add_exclude %s\n", line);
113                add_fname_to_exclude_list(ff, line);
114             }
115             fclose(fd);
116             break;
117
118          case 'i':                    /* include list */
119             if ((fd = fopen(optarg, "r")) == NULL) {
120                Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
121                   optarg, strerror(errno));
122                exit(1);
123             }
124             while (fgets(line, sizeof(line), fd) != NULL) {
125                strip_trailing_junk(line);
126                Dmsg1(900, "add_include %s\n", line);
127                add_fname_to_include_list(ff, 0, line);
128             }
129             fclose(fd);
130             got_inc = TRUE;
131             break;
132
133          case '?':
134          default:
135             usage();
136
137       }  
138    }
139    argc -= optind;
140    argv += optind;
141
142    if (argc != 2) {
143       Pmsg0(0, "Wrong number of arguments: \n");
144       usage();
145    }
146    if (!got_inc) {                            /* If no include file, */
147       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
148    }
149
150    where = argv[1];
151    do_extract(argv[0]);
152
153    if (bsr) {
154       free_bsr(bsr);
155    }
156    return 0;
157 }
158
159 static void do_extract(char *devname)
160 {
161
162    jcr = setup_jcr("bextract", devname, bsr);
163    dev = setup_to_read_device(jcr);
164    if (!dev) {
165       exit(1);
166    }
167
168    /* Make sure where directory exists and that it is a directory */
169    if (stat(where, &statp) < 0) {
170       Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n",
171          where, strerror(errno));
172    }
173    if (!S_ISDIR(statp.st_mode)) {
174       Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where);
175    }
176
177    wherelen = strlen(where);
178    fname = get_pool_memory(PM_FNAME);
179    ofile = get_pool_memory(PM_FNAME);
180    lname = get_pool_memory(PM_FNAME);
181
182
183
184
185    compress_buf = get_memory(compress_buf_size);
186
187    read_records(jcr, dev, record_cb, mount_next_read_volume);
188    /* If output file is still open, it was the last one in the
189     * archive since we just hit an end of file, so close the file. 
190     */
191    if (ofd >= 0) {
192       close(ofd);
193       set_statp(jcr, fname, ofile, lname, type, &statp);
194    }
195    release_device(jcr, dev);
196
197    free_pool_memory(fname);
198    free_pool_memory(ofile);
199    free_pool_memory(lname);
200    free_pool_memory(compress_buf);
201    term_dev(dev);
202    free_jcr(jcr);
203    printf("%u files restored.\n", num_files);
204    return;
205 }
206
207 /*
208  * Called here for each record from read_records()
209  */
210 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
211 {
212    if (rec->FileIndex < 0) {
213       return;                         /* we don't want labels */
214    }
215
216    /* File Attributes stream */
217    if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
218       char *ap, *lp, *fp;
219
220       /* If extracting, it was from previous stream, so
221        * close the output file.
222        */
223       if (extract) {
224          if (ofd < 0) {
225             Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
226          }
227          close(ofd);
228          ofd = -1;
229          extract = FALSE;
230          set_statp(jcr, fname, ofile, lname, type, &statp);
231       }
232
233       if (sizeof_pool_memory(fname) < rec->data_len) {
234          fname = realloc_pool_memory(fname, rec->data_len + 1);
235       }
236       if (sizeof_pool_memory(ofile) < rec->data_len + wherelen + 1) {
237          ofile = realloc_pool_memory(ofile, rec->data_len + wherelen + 1);
238       }
239       if (sizeof_pool_memory(lname) < rec->data_len) {
240          lname = realloc_pool_memory(lname, rec->data_len + wherelen + 1);
241       }
242       *fname = 0;
243       *lname = 0;
244
245       /*              
246        * An Attributes record consists of:
247        *    File_index
248        *    Type   (FT_types)
249        *    Filename
250        *    Attributes
251        *    Link name (if file linked i.e. FT_LNK)
252        *
253        */
254       sscanf(rec->data, "%ld %d", &record_file_index, &type);
255       if (record_file_index != rec->FileIndex)
256          Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
257             rec->FileIndex, record_file_index);
258       ap = rec->data;
259       while (*ap++ != ' ')         /* skip record file index */
260          ;
261       while (*ap++ != ' ')         /* skip type */
262          ;
263       /* Save filename and position to attributes */
264       fp = fname;
265       while (*ap != 0) {
266          *fp++  = *ap++;
267       }
268       *fp = *ap++;                 /* terminate filename & point to attribs */
269
270       /* Skip to Link name */
271       if (type == FT_LNK || type == FT_LNKSAVED) {
272          lp = ap;
273          while (*lp++ != 0) {
274             ;
275          }
276       } else {
277          lp = "";
278       }
279
280          
281       if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
282
283          decode_stat(ap, &statp);
284          /*
285           * Prepend the where directory so that the
286           * files are put where the user wants.
287           *
288           * We do a little jig here to handle Win32 files with
289           * a drive letter.  
290           *   If where is null and we are running on a win32 client,
291           *      change nothing.
292           *   Otherwise, if the second character of the filename is a
293           *   colon (:), change it into a slash (/) -- this creates
294           *   a reasonable pathname on most systems.
295           */
296          if (where[0] == 0 && win32_client) {
297             strcpy(ofile, fname);
298             strcpy(lname, lp);
299          } else {
300             strcpy(ofile, where);
301             if (fname[1] == ':') {
302                fname[1] = '/';
303                strcat(ofile, fname);
304                fname[1] = ':';
305             } else {
306                strcat(ofile, fname);
307             }
308             /* Fixup link name */
309             if (type == FT_LNK || type == FT_LNKSAVED) {
310                if (lp[0] == '/') {      /* if absolute path */
311                   strcpy(lname, where);
312                }       
313                /* ***FIXME**** we shouldn't have links on Windoz */
314                if (lp[1] == ':') {
315                   lp[1] = '/';
316                   strcat(lname, lp);
317                   lp[1] = ':';
318                } else {
319                   strcat(lname, lp);
320                }
321             }
322          }
323
324            /*          Pmsg1(000, "Restoring: %s\n", ofile); */
325
326          extract = create_file(jcr, fname, ofile, lname, type, &statp, &ofd);
327          num_files++;
328
329          if (extract) {
330              print_ls_output(ofile, lname, type, &statp);   
331          }
332       }
333
334    /* Data stream and extracting */
335    } else if (rec->Stream == STREAM_FILE_DATA) {
336       if (extract) {
337          total += rec->data_len;
338          Dmsg2(8, "Write %ld bytes, total=%ld\n", rec->data_len, total);
339          if ((uint32_t)write(ofd, rec->data, rec->data_len) != rec->data_len) {
340             Emsg1(M_ERROR_TERM, 0, "Write error: %s\n", strerror(errno));
341          }
342       }
343
344    } else if (rec->Stream == STREAM_GZIP_DATA) {
345 #ifdef HAVE_LIBZ
346       if (extract) {
347          uLongf compress_len;
348
349          compress_len = compress_buf_size;
350          if (uncompress((Bytef *)compress_buf, &compress_len, 
351                (const Bytef *)rec->data, (uLong)rec->data_len) != Z_OK) {
352             Emsg0(M_ERROR_TERM, 0, _("Uncompression error.\n"));
353          }
354
355          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
356          if ((uLongf)write(ofd, compress_buf, (size_t)compress_len) != compress_len) {
357             Pmsg0(0, "===Write error===\n");
358             Emsg2(M_ERROR_TERM, 0, "Write error on %s: %s\n", ofile, strerror(errno));
359          }
360          total += compress_len;
361          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
362             compress_len);
363       }
364 #else
365       if (extract) {
366          Emsg0(M_ERROR_TERM, 0, "GZIP data stream found, but GZIP not configured!\n");
367       }
368 #endif
369
370
371    /* If extracting, wierd stream (not 1 or 2), close output file anyway */
372    } else if (extract) {
373       if (ofd < 0) {
374          Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
375       }
376       close(ofd);
377       ofd = -1;
378       extract = FALSE;
379       set_statp(jcr, fname, ofile, lname, type, &statp);
380    } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
381       Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
382    }
383 }
384
385
386
387
388 /* Dummies to replace askdir.c */
389 int     dir_get_volume_info(JCR *jcr, int writing) { return 1;}
390 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
391 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
392 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
393 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
394 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
395 int     dir_send_job_status(JCR *jcr) {return 1;}
396
397
398 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
399 {
400    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
401       jcr->VolumeName, dev_name(dev));
402    getchar();   
403    return 1;
404 }