]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bextract.c
d6b8de960586ae1296b4a2f24fb7a91e45e6d580
[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
35 static void do_extract(char *fname, char *prefix);
36 static void print_ls_output(char *fname, char *link, int type, struct stat *statp);
37
38
39 static DEVICE *dev = NULL;
40 static int ofd = -1;
41
42 static JCR *jcr;
43 static FF_PKT my_ff;
44 static FF_PKT *ff = &my_ff;
45
46 static BSR *bsr = NULL;
47
48 static DEV_RECORD *rec;
49 static DEV_BLOCK *block;
50
51 static void usage()
52 {
53    fprintf(stderr,
54 "Usage: bextract [-d debug_level] <bacula-archive> <directory-to-store-files>\n"
55 "       -b <file>       specify a bootstrap file\n"
56 "       -dnn            set debug level to nn\n"
57 "       -e <file>       exclude list\n"
58 "       -i <file>       include list\n"
59 "       -?              print this message\n\n");
60    exit(1);
61 }
62
63 static void my_free_jcr(JCR *jcr)
64 {
65    return;
66 }
67
68
69 int main (int argc, char *argv[])
70 {
71    int ch;   
72    FILE *fd;
73    char line[1000];
74    int got_inc = FALSE;
75
76    my_name_is(argc, argv, "bextract");
77    init_msg(NULL, NULL);              /* setup message handler */
78
79    memset(ff, 0, sizeof(FF_PKT));
80    init_include_exclude_files(ff);
81
82    while ((ch = getopt(argc, argv, "b:d:e:i:?")) != -1) {
83       switch (ch) {
84          case 'b':                    /* bootstrap file */
85             bsr = parse_bsr(NULL, optarg);
86 //          dump_bsr(bsr);
87             break;
88
89          case 'd':                    /* debug level */
90             debug_level = atoi(optarg);
91             if (debug_level <= 0)
92                debug_level = 1; 
93             break;
94
95          case 'e':                    /* exclude list */
96             if ((fd = fopen(optarg, "r")) == NULL) {
97                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
98                   optarg, strerror(errno));
99                exit(1);
100             }
101             while (fgets(line, sizeof(line), fd) != NULL) {
102                strip_trailing_junk(line);
103                Dmsg1(900, "add_exclude %s\n", line);
104                add_fname_to_exclude_list(ff, line);
105             }
106             fclose(fd);
107             break;
108
109          case 'i':                    /* include list */
110             if ((fd = fopen(optarg, "r")) == NULL) {
111                Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
112                   optarg, strerror(errno));
113                exit(1);
114             }
115             while (fgets(line, sizeof(line), fd) != NULL) {
116                strip_trailing_junk(line);
117                Dmsg1(900, "add_include %s\n", line);
118                add_fname_to_include_list(ff, 0, line);
119             }
120             fclose(fd);
121             got_inc = TRUE;
122             break;
123
124          case '?':
125          default:
126             usage();
127
128       }  
129    }
130    argc -= optind;
131    argv += optind;
132
133    if (argc != 2) {
134       Pmsg0(0, "Wrong number of arguments: \n");
135       usage();
136    }
137    if (!got_inc) {                            /* If no include file, */
138       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
139    }
140
141    jcr = new_jcr(sizeof(JCR), my_free_jcr);
142    jcr->VolSessionId = 1;
143    jcr->VolSessionTime = (uint32_t)time(NULL);
144    jcr->bsr = bsr;
145    strcpy(jcr->Job, "bextract");
146    jcr->dev_name = get_pool_memory(PM_FNAME);
147    strcpy(jcr->dev_name, argv[0]);
148
149    do_extract(argv[0], argv[1]);
150
151    free_jcr(jcr);
152    if (bsr) {
153       free_bsr(bsr);
154    }
155    return 0;
156 }
157   
158
159 static void do_extract(char *devname, char *where)
160 {
161    char VolName[100];
162    char *p;
163    struct stat statp;
164    int extract = FALSE;
165    int type;
166    long record_file_index;
167    long total = 0;
168    POOLMEM *fname;                    /* original file name */
169    POOLMEM *ofile;                    /* output name with prefix */
170    POOLMEM *lname;                    /* link name */
171    int wherelen;                      /* prefix length */
172    SESSION_LABEL sessrec;
173
174    if (strncmp(devname, "/dev/", 5) != 0) {
175       /* Try stripping file part */
176       p = devname + strlen(devname);
177       while (p >= devname && *p != '/') {
178          p--;
179       }
180       if (*p == '/') {
181          strcpy(VolName, p+1);
182          *p = 0;
183       }
184    }
185    strcpy(jcr->VolumeName, VolName);
186
187    dev = init_dev(NULL, devname);
188    if (!dev || !open_device(dev)) {
189       Emsg1(M_ERROR_TERM, 0, "Cannot open %s\n", devname);
190    }
191    Dmsg0(90, "Device opened for read.\n");
192
193    if (stat(where, &statp) < 0) {
194       Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n",
195          where, strerror(errno));
196    }
197    if (!S_ISDIR(statp.st_mode)) {
198       Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where);
199    }
200
201    wherelen = strlen(where);
202    fname = get_pool_memory(PM_FNAME);
203    ofile = get_pool_memory(PM_FNAME);
204    lname = get_pool_memory(PM_FNAME);
205
206    block = new_block(dev);
207
208    create_vol_list(jcr);
209
210    Dmsg1(20, "Found %d volumes names to restore.\n", jcr->NumVolumes);
211
212    /* 
213     * Ready device for reading, and read records
214     */
215    if (!acquire_device_for_read(jcr, dev, block)) {
216       free_block(block);
217       free_vol_list(jcr);
218       return;
219    }
220
221    rec = new_record();
222    free_pool_memory(rec->data);
223    rec->data = get_memory(70000);
224
225    uint32_t compress_buf_size = 70000;
226    POOLMEM *compress_buf = get_memory(compress_buf_size);
227
228    for ( ;; ) {
229       if (!read_block_from_device(dev, block)) {
230          uint32_t status;
231          Dmsg1(500, "Main read record failed. rem=%d\n", rec->remainder);
232          if (dev->state & ST_EOT) {
233             if (!mount_next_read_volume(jcr, dev, block)) {
234                break;
235             }
236             continue;
237          }
238          if (dev->state & ST_EOF) {
239             continue;                 /* try again */
240          }
241          if (dev->state & ST_SHORT) {
242             continue;
243          }
244          Pmsg0(0, "Read Record got a bad record\n");
245          status_dev(dev, &status);
246          Dmsg1(20, "Device status: %x\n", status);
247          if (status & MT_EOD)
248             Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
249          else if (status & MT_EOT)
250             Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
251          else if (status & MT_EOF)
252             Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
253          else if (status & MT_DR_OPEN)
254             Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
255          else if (!(status & MT_ONLINE))
256             Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
257          else
258             Emsg3(M_ERROR_TERM, 0, "Read error %d on Record Header %s: %s\n", 
259                status, dev_name(dev), strerror(errno));
260       }
261
262       for (rec->state=0; !is_block_empty(rec); ) {
263          if (!read_record_from_block(block, rec)) {
264             break;
265          }
266
267          /* This is no longer used */                
268          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
269             Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
270             break;                       /* END OF FILE */
271          }
272
273          /* 
274           * Check for Start or End of Session Record 
275           *
276           */
277          if (rec->FileIndex < 0) {
278             char *rtype;
279             memset(&sessrec, 0, sizeof(sessrec));
280             switch (rec->FileIndex) {
281                case PRE_LABEL:
282                   rtype = "Fresh Volume Label";   
283                   break;
284                case VOL_LABEL:
285                   rtype = "Volume Label";
286                   unser_volume_label(dev, rec);
287                   break;
288                case SOS_LABEL:
289                   rtype = "Begin Session";
290                   unser_session_label(&sessrec, rec);
291                   break;
292                case EOS_LABEL:
293                   rtype = "End Session";
294                   break;
295                case EOM_LABEL:
296                   rtype = "End of Media";
297                   break;
298                default:
299                   rtype = "Unknown";
300                   break;
301             }
302             if (debug_level > 0) {
303                printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
304                   rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
305             }
306
307             Dmsg1(40, "Got label = %d\n", rec->FileIndex);
308             if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
309                Dmsg0(40, "Get EOM LABEL\n");
310                break;                         /* yes, get out */
311             }
312             continue;                         /* ignore other labels */
313          } /* end if label record */
314
315          /* Is this the file we want? */
316          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
317             rec->remainder = 0;
318             continue;
319          }
320          if (is_partial_record(rec)) {
321             break;
322          }
323
324          /* File Attributes stream */
325          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
326             char *ap, *lp, *fp;
327
328             /* If extracting, it was from previous stream, so
329              * close the output file.
330              */
331             if (extract) {
332                if (ofd < 0) {
333                   Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
334                }
335                close(ofd);
336                ofd = -1;
337                extract = FALSE;
338                set_statp(jcr, fname, ofile, lname, type, &statp);
339             }
340
341             if (sizeof_pool_memory(fname) < rec->data_len) {
342                fname = realloc_pool_memory(fname, rec->data_len + 1);
343             }
344             if (sizeof_pool_memory(ofile) < sizeof_pool_memory(fname) + wherelen + 1) {
345                ofile = realloc_pool_memory(ofile, sizeof_pool_memory(fname) + wherelen + 1);
346             }
347             if (sizeof_pool_memory(lname) < rec->data_len) {
348                lname = realloc_pool_memory(lname, rec->data_len + 1);
349             }
350             *fname = 0;
351             *lname = 0;
352
353             /*              
354              * An Attributes record consists of:
355              *    File_index
356              *    Type   (FT_types)
357              *    Filename
358              *    Attributes
359              *    Link name (if file linked i.e. FT_LNK)
360              *
361              */
362             sscanf(rec->data, "%ld %d", &record_file_index, &type);
363             if (record_file_index != rec->FileIndex)
364                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
365                   rec->FileIndex, record_file_index);
366             ap = rec->data;
367             while (*ap++ != ' ')         /* skip record file index */
368                ;
369             while (*ap++ != ' ')         /* skip type */
370                ;
371             /* Save filename and position to attributes */
372             fp = fname;
373             while (*ap != 0) {
374                *fp++  = *ap++;
375             }
376             *fp = *ap++;                 /* terminate filename & point to attribs */
377
378             /* Skip to Link name */
379             if (type == FT_LNK) {
380                lp = ap;
381                while (*lp++ != 0) {
382                   ;
383                }
384                strcat(lname, lp);        /* "save" link name */
385             } else {
386                *lname = 0;
387             }
388
389                
390             if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
391
392                decode_stat(ap, &statp);
393                /*
394                 * Prepend the where directory so that the
395                 * files are put where the user wants.
396                 *
397                 * We do a little jig here to handle Win32 files with
398                 * a drive letter.  
399                 *   If where is null and we are running on a win32 client,
400                 *      change nothing.
401                 *   Otherwise, if the second character of the filename is a
402                 *   colon (:), change it into a slash (/) -- this creates
403                 *   a reasonable pathname on most systems.
404                 */
405                strcpy(ofile, where);
406                if (fname[1] == ':') {
407                   fname[1] = '/';
408                   strcat(ofile, fname);
409                   fname[1] = ':';
410                } else {
411                   strcat(ofile, fname);
412                }
413    /*          Pmsg1(000, "Restoring: %s\n", ofile); */
414
415                extract = create_file(jcr, fname, ofile, lname, type, &statp, &ofd);
416
417                if (extract) {
418                    print_ls_output(ofile, lname, type, &statp);   
419                }
420             }
421
422          /* Data stream and extracting */
423          } else if (rec->Stream == STREAM_FILE_DATA) {
424             if (extract) {
425                total += rec->data_len;
426                Dmsg2(8, "Write %ld bytes, total=%ld\n", rec->data_len, total);
427                if ((uint32_t)write(ofd, rec->data, rec->data_len) != rec->data_len) {
428                   Emsg1(M_ERROR_TERM, 0, "Write error: %s\n", strerror(errno));
429                }
430             }
431     
432          } else if (rec->Stream == STREAM_GZIP_DATA) {
433 #ifdef HAVE_LIBZ
434             if (extract) {
435                uLongf compress_len;
436
437                compress_len = compress_buf_size;
438                if (uncompress((Bytef *)compress_buf, &compress_len, 
439                      (const Bytef *)rec->data, (uLong)rec->data_len) != Z_OK) {
440                   Emsg0(M_ERROR_TERM, 0, _("Uncompression error.\n"));
441                }
442
443                Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
444                if ((uLongf)write(ofd, compress_buf, (size_t)compress_len) != compress_len) {
445                   Pmsg0(0, "===Write error===\n");
446                   Emsg2(M_ERROR_TERM, 0, "Write error on %s: %s\n", ofile, strerror(errno));
447                }
448                total += 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_TERM, 0, "GZIP data stream found, but GZIP not configured!\n");
455             }
456 #endif
457
458
459          /* If extracting, wierd stream (not 1 or 2), close output file anyway */
460          } else if (extract) {
461             if (ofd < 0) {
462                Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
463             }
464             close(ofd);
465             ofd = -1;
466             extract = FALSE;
467             set_statp(jcr, fname, ofile, lname, type, &statp);
468          } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
469             Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
470          }
471       }
472    }
473
474
475    /* If output file is still open, it was the last one in the
476     * archive since we just hit an end of file, so close the file. 
477     */
478    if (ofd >= 0) {
479       close(ofd);
480       set_statp(jcr, fname, ofile, lname, type, &statp);
481    }
482    release_device(jcr, dev, block);
483
484    free_pool_memory(fname);
485    free_pool_memory(ofile);
486    free_pool_memory(lname);
487    free_pool_memory(compress_buf);
488    term_dev(dev);
489    free_block(block);
490    free_record(rec);
491    return;
492 }
493
494 extern char *getuser(uid_t uid);
495 extern char *getgroup(gid_t gid);
496
497 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
498 {
499    char buf[1000]; 
500    char ec1[30];
501    char *p, *f;
502    int n;
503
504    p = encode_mode(statp->st_mode, buf);
505    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
506    p += n;
507    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
508    p += n;
509    n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
510    p += n;
511    p = encode_time(statp->st_ctime, p);
512    *p++ = ' ';
513    *p++ = ' ';
514    /* Copy file name */
515    for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
516       *p++ = *f++;
517    if (type == FT_LNK) {
518       *p++ = ' ';
519       *p++ = '-';
520       *p++ = '>';
521       *p++ = ' ';
522       /* Copy link name */
523       for (f=link; *f && (p-buf) < (int)sizeof(buf); )
524          *p++ = *f++;
525    }
526    *p++ = '\n';
527    *p = 0;
528    fputs(buf, stdout);
529 }
530
531 /* Dummies to replace askdir.c */
532 int     dir_get_volume_info(JCR *jcr) { return 1;}
533 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
534 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
535 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
536 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
537 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
538 int     dir_send_job_status(JCR *jcr) {return 1;}
539
540
541 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
542 {
543    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
544       jcr->VolumeName, dev_name(dev));
545    getchar();   
546    return 1;
547 }