]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
63fef7f34c5445fee88aaf150b003b30f7e175ad
[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             Dmsg2(30, "!read-break. stat=%s blk=%d\n", rec_state_to_str(rec), 
515                   block->BlockNumber);
516             break;
517          }
518          /*
519           * At this point, we have at least a record header.
520           *  Now decide if we want this record or not, but remember
521           *  before accessing the record, we may need to read again to
522           *  get all the data.
523           */
524          record++;
525          if (verbose) {
526             Dmsg6(30, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
527                rec_state_to_str(rec), block->BlockNumber,
528                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
529          }
530          if (debug_level >= 30) {
531             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
532                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
533                   rec->data_len);
534          }
535
536          /*  
537           * Check for End of File record (all zeros)
538           *    NOTE: this no longer exists
539           */
540          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
541             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
542          }
543
544          /* 
545           * Check for Start or End of Session Record 
546           *
547           */
548          if (rec->FileIndex < 0) {
549             char *rtype;
550             memset(&sessrec, 0, sizeof(sessrec));
551             switch (rec->FileIndex) {
552                case PRE_LABEL:
553                   rtype = "Fresh Volume Label";   
554                   break;
555                case VOL_LABEL:
556                   rtype = "Volume Label";
557                   unser_volume_label(dev, rec);
558                   break;
559                case SOS_LABEL:
560                   rtype = "Begin Session";
561                   unser_session_label(&sessrec, rec);
562                   break;
563                case EOS_LABEL:
564                   rtype = "End Session";
565                   break;
566                case EOM_LABEL:
567                   rtype = "End of Media";
568                   break;
569                default:
570                   rtype = "Unknown";
571                   break;
572             }
573             if (debug_level > 0) {
574                printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
575                   rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
576             }
577
578             Dmsg1(40, "Got label = %d\n", rec->FileIndex);
579             if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
580                Dmsg0(100, "EOM LABEL break\n");
581                rec->remainder = 0;
582                break;                         /* yes, get out */
583             }
584             rec->remainder = 0;
585             continue;              /* we don't want record, read next one */
586          } /* end if label record */
587
588          /* 
589           * Apply BSR filter
590           */
591          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
592             if (verbose) {
593                Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
594                   record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime, 
595                   rec->FileIndex);
596             }
597             rec->remainder = 0;
598             continue;              /* we don't want record, read next one */
599          }
600          if (is_partial_record(rec)) {
601             Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
602                rec_state_to_str(rec), block->BlockNumber,
603                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
604             break;                    /* read second part of record */
605          }
606
607          /* File Attributes stream */
608          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
609             char *ap, *fp;
610             sscanf(rec->data, "%ld %d", &record_file_index, &type);
611             if (record_file_index != rec->FileIndex) {
612                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
613                   rec->FileIndex, record_file_index);
614             }
615             ap = rec->data;
616
617             while (*ap++ != ' ')         /* skip record file index */
618                ;
619             while (*ap++ != ' ')         /* skip type */
620                ;
621             /* Save filename and position to attributes */
622             fp = fname;
623             while (*ap != 0) {
624                *fp++  = *ap++;
625             }
626             *fp = *ap++;                 /* terminate filename & point to attribs */
627
628             decode_stat(ap, &statp);
629             /* Skip to link name */  
630             while (*ap++ != 0)
631                ;
632             print_ls_output(fname, ap, type, &statp);
633             num_files++;
634          }
635       }
636    }
637    if (verbose) {
638       printf("%u files found.\n", num_files);
639    }
640    return;
641 }
642
643 extern char *getuser(uid_t uid);
644 extern char *getgroup(gid_t gid);
645
646 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
647 {
648    char buf[1000]; 
649    char ec1[30];
650    char *p, *f;
651    int n;
652
653    if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
654       return;
655    }
656    p = encode_mode(statp->st_mode, buf);
657    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
658    p += n;
659    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
660    p += n;
661    n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
662    p += n;
663    p = encode_time(statp->st_ctime, p);
664    *p++ = ' ';
665    *p++ = ' ';
666    /* Copy file name */
667    for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
668       *p++ = *f++;
669    if (type == FT_LNK) {
670       *p++ = ' ';
671       *p++ = '-';
672       *p++ = '>';
673       *p++ = ' ';
674       /* Copy link name */
675       for (f=link; *f && (p-buf) < (int)sizeof(buf); )
676          *p++ = *f++;
677    }
678    *p++ = '\n';
679    *p = 0;
680    fputs(buf, stdout);
681 }
682
683 /* Dummies to replace askdir.c */
684 int     dir_get_volume_info(JCR *jcr) { return 1;}
685 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
686 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
687 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
688 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
689 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
690 int     dir_send_job_status(JCR *jcr) {return 1;}
691
692
693 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
694 {
695    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
696       jcr->VolumeName, dev_name(dev));
697    getchar();   
698    return 1;
699 }