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