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