]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Restore from multiple simultaneous backups
[bacula/bacula] / bacula / src / stored / bls.c
1 /*
2  *
3  *  Dumb program to do an "ls" of a Bacula 1.0 mortal file.
4  *
5  *   Version $Id$
6  */
7 /*
8    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2 of
13    the License, or (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public
21    License along with this program; if not, write to the Free
22    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
23    MA 02111-1307, USA.
24
25  */
26
27 #include "bacula.h"
28 #include "stored.h"
29 #include "findlib/find.h"
30
31 static void do_blocks(char *infname);
32 static void do_jobs(char *infname);
33 static void do_ls(char *fname);
34 static void print_ls_output(char *fname, char *link, int type, struct stat *statp);
35 static void do_setup(char *infname);
36 static void do_close();
37
38 static DEVICE *dev;
39 static int default_tape = FALSE;
40 static int dump_label = FALSE;
41 static int list_blocks = FALSE;
42 static int list_jobs = FALSE;
43 static int verbose = 0;
44 static char Vol[2000];
45 static char *VolName;
46 static char *p;
47 static DEV_RECORD *rec;
48 static DEV_BLOCK *block;
49 static int NumVolumes, CurVolume;
50 static JCR *jcr;
51
52
53 extern char BaculaId[];
54
55 static FF_PKT ff;
56
57 static BSR *bsr = NULL;
58
59 static void usage()
60 {
61    fprintf(stderr,
62 "Usage: bls [-d debug_level] <physical-device-name>\n"
63 "       -b <file>       specify a bootstrap file\n"
64 "       -e <file>       exclude list\n"
65 "       -i <file>       include list\n"
66 "       -j              list jobs\n"
67 "       -k              list blocks\n"
68 "       -L              list tape label\n"
69 "    (none of above)    list saved files\n"
70 "       -t              use default tape device\n"
71 "       -v              be verbose\n"
72 "       -?              print this message\n\n");
73    exit(1);
74 }
75
76 static char *rec_state_to_str(DEV_RECORD *rec)
77 {
78    static char buf[200]; 
79    buf[0] = 0;
80    if (rec->state & REC_NO_HEADER) {
81       strcat(buf, "Nohdr,");
82    }
83    if (is_partial_record(rec)) {
84       strcat(buf, "partial,");
85    }
86    if (rec->state & REC_BLOCK_EMPTY) {
87       strcat(buf, "empty,");
88    }
89    if (rec->state & REC_NO_MATCH) {
90       strcat(buf, "Nomatch,");
91    }
92    if (rec->state & REC_CONTINUATION) {
93       strcat(buf, "cont,");
94    }
95    if (buf[0]) {
96       buf[strlen(buf)-1] = 0;
97    }
98    return buf;
99 }
100
101
102
103 int main (int argc, char *argv[])
104 {
105    int i, ch;
106    FILE *fd;
107    char line[1000];
108
109    my_name_is(argc, argv, "bls");
110    init_msg(NULL, NULL);              /* initialize message handler */
111
112    memset(&ff, 0, sizeof(ff));
113    init_include_exclude_files(&ff);
114
115    while ((ch = getopt(argc, argv, "b:d:e:i:jkLtv?")) != -1) {
116       switch (ch) {
117          case 'b':
118             bsr = parse_bsr(NULL, optarg);
119             break;
120
121          case 'd':                    /* debug level */
122             debug_level = atoi(optarg);
123             if (debug_level <= 0)
124                debug_level = 1; 
125             break;
126
127          case 'e':                    /* exclude list */
128             if ((fd = fopen(optarg, "r")) == NULL) {
129                Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
130                   optarg, strerror(errno));
131                exit(1);
132             }
133             while (fgets(line, sizeof(line), fd) != NULL) {
134                strip_trailing_junk(line);
135                Dmsg1(000, "add_exclude %s\n", line);
136                add_fname_to_exclude_list(&ff, line);
137             }
138             fclose(fd);
139             break;
140
141          case 'i':                    /* include list */
142             if ((fd = fopen(optarg, "r")) == NULL) {
143                Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
144                   optarg, strerror(errno));
145                exit(1);
146             }
147             while (fgets(line, sizeof(line), fd) != NULL) {
148                strip_trailing_junk(line);
149                Dmsg1(000, "add_include %s\n", line);
150                add_fname_to_include_list(&ff, 0, line);
151             }
152             fclose(fd);
153             break;
154
155          case 'j':
156             list_jobs = TRUE;
157             break;
158
159          case 'k':
160             list_blocks = TRUE;
161             break;
162
163          case 'L':
164             dump_label = TRUE;
165             break;
166
167          case 't':
168             default_tape = TRUE;
169             break;
170
171          case 'v':
172             verbose++;
173             break;
174
175          case '?':
176          default:
177             usage();
178
179       }  
180    }
181    argc -= optind;
182    argv += optind;
183
184    if (!argc && !default_tape) {
185       Pmsg0(0, "No archive name specified\n");
186       usage();
187    }
188
189    if (ff.included_files_list == NULL) {
190       add_fname_to_include_list(&ff, 0, "/");
191    }
192
193    /* Try default device */
194    if (default_tape) {
195       do_ls(DEFAULT_TAPE_DRIVE);
196       return 0;
197    }
198
199    for (i=0; i < argc; i++) {
200       do_setup(argv[i]);
201       if (list_blocks) {
202          do_blocks(argv[i]);
203       } else if (list_jobs) {
204          do_jobs(argv[i]);
205       } else {
206          do_ls(argv[i]);
207       }
208       do_close();
209    }
210    if (bsr) {
211       free_bsr(bsr);
212    }
213    return 0;
214 }
215
216 static void my_free_jcr(JCR *jcr)
217 {
218    return;
219 }
220
221 /*
222  * Setup device, jcr, and prepare to read
223  */
224 static void do_setup(char *infname) 
225 {
226    jcr = new_jcr(sizeof(JCR), my_free_jcr);
227    jcr->VolSessionId = 1;
228    jcr->VolSessionTime = (uint32_t)time(NULL);
229    jcr->bsr = bsr;
230    strcpy(jcr->Job, "bls");
231    jcr->dev_name = get_pool_memory(PM_FNAME);
232    strcpy(jcr->dev_name, infname);
233
234    VolName = Vol;
235    VolName[0] = 0;
236    if (strncmp(infname, "/dev/", 5) != 0) {
237       /* Try stripping file part */
238       p = infname + strlen(infname);
239       while (p >= infname && *p != '/')
240          p--;
241       if (*p == '/') {
242          strcpy(VolName, p+1);
243          *p = 0;
244       }
245    }
246    Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
247    dev = init_dev(NULL, infname);
248    if (!dev) {
249       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
250       exit(1);
251    }
252    /* ***FIXME**** init capabilities */
253    if (!open_device(dev)) {
254       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
255       exit(1);
256    }
257    Dmsg0(90, "Device opened for read.\n");
258
259    rec = new_record();
260    block = new_block(dev);
261
262    NumVolumes = 0;
263    CurVolume = 1;
264    for (p = VolName; p && *p; ) {
265       p = strchr(p, '^');
266       if (p) {
267          *p++ = 0;
268       }
269       NumVolumes++;
270    }
271
272    pm_strcpy(&jcr->VolumeName, VolName);
273    Dmsg1(100, "Volume=%s\n", jcr->VolumeName);
274    if (!acquire_device_for_read(jcr, dev, block)) {
275       Emsg0(M_ERROR, 0, dev->errmsg);
276       exit(1);
277    }
278 }
279
280 static void do_close()
281 {
282    term_dev(dev);
283    free_record(rec);
284    free_block(block);
285    free_jcr(jcr);
286 }
287
288 static int mount_next_volume(char *infname)
289 {
290    if (rec->remainder) {
291       Dmsg0(20, "Not end of record. Next volume has more data for current record.\n");
292    }
293    Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
294    if (NumVolumes > 1 && CurVolume < NumVolumes) {
295       p = VolName;
296       while (*p++)  
297          { }
298       CurVolume++;
299       Dmsg1(20, "There is another volume %s.\n", p);
300       VolName = p;
301       pm_strcpy(&jcr->VolumeName, VolName);
302
303       close_dev(dev);
304       dev->state &= ~ST_READ; 
305       if (!acquire_device_for_read(jcr, dev, block)) {
306          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
307          exit(1);
308       }
309       return 1;              /* Next volume mounted */
310    }
311    printf("End of Device reached.\n");
312    return 0;                 /* EOT */
313 }
314
315 /*
316  * Device got an error, attempt to analyse it
317  */
318 static void display_error_status()
319 {
320    uint32_t status;
321
322    Emsg0(M_ERROR, 0, dev->errmsg);
323    status_dev(dev, &status);
324    Dmsg1(20, "Device status: %x\n", status);
325    if (status & MT_EOD)
326       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
327    else if (status & MT_EOT)
328       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
329    else if (status & MT_EOF)
330       Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
331    else if (status & MT_DR_OPEN)
332       Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
333    else if (!(status & MT_ONLINE))
334       Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
335    else
336       Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
337 }
338
339
340 /* List just block information */
341 static void do_blocks(char *infname)
342 {
343
344    dump_volume_label(dev);
345
346    /* Assume that we have already read the volume label.
347     * If on second or subsequent volume, adjust buffer pointer 
348     */
349    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
350       Pmsg1(0, "\n\
351 Warning, this Volume is a continuation of Volume %s\n",
352                 dev->VolHdr.PrevVolName);
353    }
354  
355    if (verbose) {
356       rec = new_record();
357    }
358    for ( ;; ) {
359       if (!read_block_from_device(dev, block)) {
360          Dmsg0(20, "!read_block()\n");
361          if (dev->state & ST_EOT) {
362             if (!mount_next_volume(infname)) {
363                break;
364             }
365             continue;
366          }
367          if (dev->state & ST_EOF) {
368             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
369             Dmsg0(20, "read_record got eof. try again\n");
370             continue;
371          }
372          if (dev->state & ST_SHORT) {
373             Emsg0(M_INFO, 0, dev->errmsg);
374             continue;
375          }
376          display_error_status();
377          break;
378       }
379
380       if (verbose) {
381          read_record_from_block(block, rec);
382          Pmsg6(-1, "Block: %d blen=%d First rec FI=%s SessId=%d Strm=%s rlen=%d\n",
383               block->BlockNumber, block->block_len,
384               FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
385               stream_to_ascii(rec->Stream), rec->data_len);
386          rec->remainder = 0;
387       } else {
388          printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
389       }
390
391    }
392    return;
393 }
394
395 /* Do list job records */
396 static void do_jobs(char *infname)
397 {
398
399    /* Assume that we have already read the volume label.
400     * If on second or subsequent volume, adjust buffer pointer 
401     */
402    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
403       Pmsg1(0, "\n\
404 Warning, this Volume is a continuation of Volume %s\n",
405                 dev->VolHdr.PrevVolName);
406    }
407  
408    for ( ;; ) {
409       if (!read_block_from_device(dev, block)) {
410          Dmsg0(20, "!read_block()\n");
411          if (dev->state & ST_EOT) {
412             if (!mount_next_volume(infname)) {
413                break;
414             }
415             continue;
416          }
417          if (dev->state & ST_EOF) {
418             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
419             Dmsg0(20, "read_record got eof. try again\n");
420             continue;
421          }
422          if (dev->state & ST_SHORT) {
423             Pmsg0(000, "Got short block.\n");
424             Emsg0(M_INFO, 0, dev->errmsg);
425             continue;
426          }
427          display_error_status();
428          break;
429       }
430       while (read_record_from_block(block, rec)) {
431          if (debug_level >= 30) {
432             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
433                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
434                   rec->data_len);
435          }
436
437
438          /*  
439           * Check for End of File record (all zeros)
440           *    NOTE: this no longer exists
441           */
442          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
443             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
444          }
445
446          /* 
447           * Check for Start or End of Session Record 
448           *
449           */
450          if (rec->FileIndex < 0) {
451             dump_label_record(dev, rec, verbose);
452             continue;
453          }
454       }
455       rec->remainder = 0;
456    }
457    return;
458 }
459
460 /* Do an ls type listing of an archive */
461 static void do_ls(char *infname)
462 {
463    char fname[2000];
464    struct stat statp;
465    int type;
466    long record_file_index;
467    uint32_t num_files = 0;
468    int record;
469
470    if (dump_label) {
471       dump_volume_label(dev);
472       return;
473    }
474
475    /* Assume that we have already read the volume label.
476     * If on second or subsequent volume, adjust buffer pointer 
477     */
478    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
479       Pmsg1(0, "\n\
480 Warning, this Volume is a continuation of Volume %s\n",
481                 dev->VolHdr.PrevVolName);
482    }
483  
484    for ( ;; ) {
485       SESSION_LABEL sessrec;
486
487       if (!read_block_from_device(dev, block)) {
488          Dmsg0(20, "!read_record()\n");
489          if (dev->state & ST_EOT) {
490             if (!mount_next_volume(infname)) {
491                break;
492             }
493             continue;
494          }
495          if (dev->state & ST_EOF) {
496             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
497             Dmsg0(20, "read_record got eof. try again\n");
498             continue;
499          }
500          if (dev->state & ST_SHORT) {
501             Emsg0(M_INFO, 0, dev->errmsg);
502             continue;
503          }
504          display_error_status();
505          break;
506       }
507       if (verbose) {
508          Dmsg2(10, "Block: %d blen=%d\n", block->BlockNumber, block->block_len);
509       }
510
511       record = 0;
512       for (rec->state=0; !is_block_empty(rec); ) {
513          if (!read_record_from_block(block, rec)) {
514             Dmsg3(10, "!read-break. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
515                   block->BlockNumber, rec->remainder);
516             break;
517          }
518          Dmsg3(10, "read-OK. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec), 
519                   block->BlockNumber, rec->remainder);
520          /*
521           * At this point, we have at least a record header.
522           *  Now decide if we want this record or not, but remember
523           *  before accessing the record, we may need to read again to
524           *  get all the data.
525           */
526          record++;
527          if (verbose) {
528             Dmsg6(30, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
529                rec_state_to_str(rec), block->BlockNumber,
530                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
531          }
532          if (debug_level >= 30) {
533             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
534                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
535                   rec->data_len);
536          }
537
538          /*  
539           * Check for End of File record (all zeros)
540           *    NOTE: this no longer exists
541           */
542          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
543             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
544          }
545
546          /* 
547           * Check for Start or End of Session Record 
548           *
549           */
550          if (rec->FileIndex < 0) {
551             char *rtype;
552             memset(&sessrec, 0, sizeof(sessrec));
553             switch (rec->FileIndex) {
554                case PRE_LABEL:
555                   rtype = "Fresh Volume Label";   
556                   break;
557                case VOL_LABEL:
558                   rtype = "Volume Label";
559                   unser_volume_label(dev, rec);
560                   break;
561                case SOS_LABEL:
562                   rtype = "Begin Session";
563                   unser_session_label(&sessrec, rec);
564                   break;
565                case EOS_LABEL:
566                   rtype = "End Session";
567                   break;
568                case EOM_LABEL:
569                   rtype = "End of Media";
570                   break;
571                default:
572                   rtype = "Unknown";
573                   break;
574             }
575             if (debug_level > 0) {
576                printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
577                   rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
578             }
579
580             Dmsg1(40, "Got label = %d\n", rec->FileIndex);
581             if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
582                Dmsg0(100, "EOM LABEL break\n");
583                rec->remainder = 0;
584                break;                         /* yes, get out */
585             }
586             rec->remainder = 0;
587             continue;              /* we don't want record, read next one */
588          } /* end if label record */
589
590          /* 
591           * Apply BSR filter
592           */
593          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
594             if (verbose) {
595                Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
596                   record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime, 
597                   rec->FileIndex);
598             }
599             rec->remainder = 0;
600             continue;              /* we don't want record, read next one */
601          }
602          if (is_partial_record(rec)) {
603             Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
604                rec_state_to_str(rec), block->BlockNumber,
605                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
606             break;                    /* read second part of record */
607          }
608
609          /* File Attributes stream */
610          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
611             char *ap, *fp;
612             sscanf(rec->data, "%ld %d", &record_file_index, &type);
613             if (record_file_index != rec->FileIndex) {
614                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
615                   rec->FileIndex, record_file_index);
616             }
617             ap = rec->data;
618
619             while (*ap++ != ' ')         /* skip record file index */
620                ;
621             while (*ap++ != ' ')         /* skip type */
622                ;
623             /* Save filename and position to attributes */
624             fp = fname;
625             while (*ap != 0) {
626                *fp++  = *ap++;
627             }
628             *fp = *ap++;                 /* terminate filename & point to attribs */
629
630             decode_stat(ap, &statp);
631             /* Skip to link name */  
632             while (*ap++ != 0)
633                ;
634             print_ls_output(fname, ap, type, &statp);
635             num_files++;
636          }
637       }
638    }
639    if (verbose) {
640       printf("%u files found.\n", num_files);
641    }
642    return;
643 }
644
645 extern char *getuser(uid_t uid);
646 extern char *getgroup(gid_t gid);
647
648 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
649 {
650    char buf[1000]; 
651    char ec1[30];
652    char *p, *f;
653    int n;
654
655    if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
656       return;
657    }
658    p = encode_mode(statp->st_mode, buf);
659    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
660    p += n;
661    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
662    p += n;
663    n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
664    p += n;
665    p = encode_time(statp->st_ctime, p);
666    *p++ = ' ';
667    *p++ = ' ';
668    /* Copy file name */
669    for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
670       *p++ = *f++;
671    if (type == FT_LNK) {
672       *p++ = ' ';
673       *p++ = '-';
674       *p++ = '>';
675       *p++ = ' ';
676       /* Copy link name */
677       for (f=link; *f && (p-buf) < (int)sizeof(buf); )
678          *p++ = *f++;
679    }
680    *p++ = '\n';
681    *p = 0;
682    fputs(buf, stdout);
683 }
684
685 /* Dummies to replace askdir.c */
686 int     dir_get_volume_info(JCR *jcr) { return 1;}
687 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
688 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
689 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
690 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
691 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
692 int     dir_send_job_status(JCR *jcr) {return 1;}
693
694
695 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
696 {
697    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
698       jcr->VolumeName, dev_name(dev));
699    getchar();   
700    return 1;
701 }