]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
011e8495883edeec83cb2ef589967cd7a95e5a47
[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    if (!acquire_device_for_read(jcr, dev, block)) {
274       Emsg0(M_ERROR, 0, dev->errmsg);
275       exit(1);
276    }
277 }
278
279 static void do_close()
280 {
281    term_dev(dev);
282    free_record(rec);
283    free_block(block);
284    free_jcr(jcr);
285 }
286
287 static int mount_next_volume(char *infname)
288 {
289    if (rec->remainder) {
290       Dmsg0(20, "Not end of record. Next volume has more data for current record.\n");
291    }
292    Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
293    if (NumVolumes > 1 && CurVolume < NumVolumes) {
294       p = VolName;
295       while (*p++)  
296          { }
297       CurVolume++;
298       Dmsg1(20, "There is another volume %s.\n", p);
299       VolName = p;
300       pm_strcpy(&jcr->VolumeName, VolName);
301
302       close_dev(dev);
303       dev->state &= ~ST_READ; 
304       if (!acquire_device_for_read(jcr, dev, block)) {
305          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
306          exit(1);
307       }
308       return 1;              /* Next volume mounted */
309    }
310    printf("End of Device reached.\n");
311    return 0;                 /* EOT */
312 }
313
314 /*
315  * Device got an error, attempt to analyse it
316  */
317 static void display_error_status()
318 {
319    uint32_t status;
320
321    Emsg0(M_ERROR, 0, dev->errmsg);
322    status_dev(dev, &status);
323    Dmsg1(20, "Device status: %x\n", status);
324    if (status & MT_EOD)
325       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
326    else if (status & MT_EOT)
327       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
328    else if (status & MT_EOF)
329       Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
330    else if (status & MT_DR_OPEN)
331       Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
332    else if (!(status & MT_ONLINE))
333       Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
334    else
335       Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
336 }
337
338
339 /* List just block information */
340 static void do_blocks(char *infname)
341 {
342
343    dump_volume_label(dev);
344
345    /* Assume that we have already read the volume label.
346     * If on second or subsequent volume, adjust buffer pointer 
347     */
348    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
349       Pmsg1(0, "\n\
350 Warning, this Volume is a continuation of Volume %s\n",
351                 dev->VolHdr.PrevVolName);
352    }
353  
354    if (verbose) {
355       rec = new_record();
356    }
357    for ( ;; ) {
358       if (!read_block_from_device(dev, block)) {
359          Dmsg0(20, "!read_block()\n");
360          if (dev->state & ST_EOT) {
361             if (!mount_next_volume(infname)) {
362                break;
363             }
364             continue;
365          }
366          if (dev->state & ST_EOF) {
367             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
368             Dmsg0(20, "read_record got eof. try again\n");
369             continue;
370          }
371          if (dev->state & ST_SHORT) {
372             Emsg0(M_INFO, 0, dev->errmsg);
373             continue;
374          }
375          display_error_status();
376          break;
377       }
378
379       if (verbose) {
380          read_record_from_block(block, rec);
381          Pmsg6(-1, "Block: %d blen=%d First rec FI=%s SessId=%d Strm=%s rlen=%d\n",
382               block->BlockNumber, block->block_len,
383               FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
384               stream_to_ascii(rec->Stream), rec->data_len);
385          rec->remainder = 0;
386       } else {
387          printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
388       }
389
390    }
391    return;
392 }
393
394 /* Do list job records */
395 static void do_jobs(char *infname)
396 {
397
398    /* Assume that we have already read the volume label.
399     * If on second or subsequent volume, adjust buffer pointer 
400     */
401    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
402       Pmsg1(0, "\n\
403 Warning, this Volume is a continuation of Volume %s\n",
404                 dev->VolHdr.PrevVolName);
405    }
406  
407    for ( ;; ) {
408       if (!read_block_from_device(dev, block)) {
409          Dmsg0(20, "!read_block()\n");
410          if (dev->state & ST_EOT) {
411             if (!mount_next_volume(infname)) {
412                break;
413             }
414             continue;
415          }
416          if (dev->state & ST_EOF) {
417             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
418             Dmsg0(20, "read_record got eof. try again\n");
419             continue;
420          }
421          if (dev->state & ST_SHORT) {
422             Pmsg0(000, "Got short block.\n");
423             Emsg0(M_INFO, 0, dev->errmsg);
424             continue;
425          }
426          display_error_status();
427          break;
428       }
429       while (read_record_from_block(block, rec)) {
430          if (debug_level >= 30) {
431             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
432                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
433                   rec->data_len);
434          }
435
436
437          /*  
438           * Check for End of File record (all zeros)
439           *    NOTE: this no longer exists
440           */
441          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
442             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
443          }
444
445          /* 
446           * Check for Start or End of Session Record 
447           *
448           */
449          if (rec->FileIndex < 0) {
450             dump_label_record(dev, rec, verbose);
451             continue;
452          }
453       }
454       rec->remainder = 0;
455    }
456    return;
457 }
458
459 /* Do an ls type listing of an archive */
460 static void do_ls(char *infname)
461 {
462    char fname[2000];
463    struct stat statp;
464    int type;
465    long record_file_index;
466    uint32_t num_files = 0;
467    int record;
468
469    if (dump_label) {
470       dump_volume_label(dev);
471       return;
472    }
473
474    /* Assume that we have already read the volume label.
475     * If on second or subsequent volume, adjust buffer pointer 
476     */
477    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
478       Pmsg1(0, "\n\
479 Warning, this Volume is a continuation of Volume %s\n",
480                 dev->VolHdr.PrevVolName);
481    }
482  
483    for ( ;; ) {
484       SESSION_LABEL sessrec;
485
486       if (!read_block_from_device(dev, block)) {
487          Dmsg0(20, "!read_record()\n");
488          if (dev->state & ST_EOT) {
489             if (!mount_next_volume(infname)) {
490                break;
491             }
492             continue;
493          }
494          if (dev->state & ST_EOF) {
495             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
496             Dmsg0(20, "read_record got eof. try again\n");
497             continue;
498          }
499          if (dev->state & ST_SHORT) {
500             Emsg0(M_INFO, 0, dev->errmsg);
501             continue;
502          }
503          display_error_status();
504          break;
505       }
506       if (verbose) {
507          Dmsg2(10, "Block: %d blen=%d\n", block->BlockNumber, block->block_len);
508       }
509
510       record = 0;
511       for (rec->state=0; !is_block_empty(rec); ) {
512          if (!read_record_from_block(block, rec)) {
513             Dmsg2(30, "!read-break. stat=%s blk=%d\n", rec_state_to_str(rec), 
514                   block->BlockNumber);
515             break;
516          }
517          /*
518           * At this point, we have at least a record header.
519           *  Now decide if we want this record or not, but remember
520           *  before accessing the record, we may need to read again to
521           *  get all the data.
522           */
523          record++;
524          if (verbose) {
525             Dmsg6(30, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
526                rec_state_to_str(rec), block->BlockNumber,
527                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
528          }
529          if (debug_level >= 30) {
530             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
531                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
532                   rec->data_len);
533          }
534
535          /*  
536           * Check for End of File record (all zeros)
537           *    NOTE: this no longer exists
538           */
539          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
540             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
541          }
542
543          /* 
544           * Check for Start or End of Session Record 
545           *
546           */
547          if (rec->FileIndex < 0) {
548             char *rtype;
549             memset(&sessrec, 0, sizeof(sessrec));
550             switch (rec->FileIndex) {
551                case PRE_LABEL:
552                   rtype = "Fresh Volume Label";   
553                   break;
554                case VOL_LABEL:
555                   rtype = "Volume Label";
556                   unser_volume_label(dev, rec);
557                   break;
558                case SOS_LABEL:
559                   rtype = "Begin Session";
560                   unser_session_label(&sessrec, rec);
561                   break;
562                case EOS_LABEL:
563                   rtype = "End Session";
564                   break;
565                case EOM_LABEL:
566                   rtype = "End of Media";
567                   break;
568                default:
569                   rtype = "Unknown";
570                   break;
571             }
572             if (debug_level > 0) {
573                printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
574                   rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
575             }
576
577             Dmsg1(40, "Got label = %d\n", rec->FileIndex);
578             if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
579                Dmsg0(100, "EOM LABEL break\n");
580                rec->remainder = 0;
581                break;                         /* yes, get out */
582             }
583             rec->remainder = 0;
584             continue;              /* we don't want record, read next one */
585          } /* end if label record */
586
587          /* 
588           * Apply BSR filter
589           */
590          if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
591             if (verbose) {
592                Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
593                   record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime, 
594                   rec->FileIndex);
595             }
596             rec->remainder = 0;
597             continue;              /* we don't want record, read next one */
598          }
599          if (is_partial_record(rec)) {
600             Dmsg6(10, "Partial, break. recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
601                rec_state_to_str(rec), block->BlockNumber,
602                rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
603             break;                    /* read second part of record */
604          }
605
606          /* File Attributes stream */
607          if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
608             char *ap, *fp;
609             sscanf(rec->data, "%ld %d", &record_file_index, &type);
610             if (record_file_index != rec->FileIndex) {
611                Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
612                   rec->FileIndex, record_file_index);
613             }
614             ap = rec->data;
615
616             while (*ap++ != ' ')         /* skip record file index */
617                ;
618             while (*ap++ != ' ')         /* skip type */
619                ;
620             /* Save filename and position to attributes */
621             fp = fname;
622             while (*ap != 0) {
623                *fp++  = *ap++;
624             }
625             *fp = *ap++;                 /* terminate filename & point to attribs */
626
627             decode_stat(ap, &statp);
628             /* Skip to link name */  
629             while (*ap++ != 0)
630                ;
631             print_ls_output(fname, ap, type, &statp);
632             num_files++;
633          }
634       }
635    }
636    if (verbose) {
637       printf("%u files found.\n", num_files);
638    }
639    return;
640 }
641
642 extern char *getuser(uid_t uid);
643 extern char *getgroup(gid_t gid);
644
645 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
646 {
647    char buf[1000]; 
648    char ec1[30];
649    char *p, *f;
650    int n;
651
652    if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
653       return;
654    }
655    p = encode_mode(statp->st_mode, buf);
656    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
657    p += n;
658    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
659    p += n;
660    n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
661    p += n;
662    p = encode_time(statp->st_ctime, p);
663    *p++ = ' ';
664    *p++ = ' ';
665    /* Copy file name */
666    for (f=fname; *f && (p-buf) < (int)sizeof(buf); )
667       *p++ = *f++;
668    if (type == FT_LNK) {
669       *p++ = ' ';
670       *p++ = '-';
671       *p++ = '>';
672       *p++ = ' ';
673       /* Copy link name */
674       for (f=link; *f && (p-buf) < (int)sizeof(buf); )
675          *p++ = *f++;
676    }
677    *p++ = '\n';
678    *p = 0;
679    fputs(buf, stdout);
680 }
681
682 /* Dummies to replace askdir.c */
683 int     dir_get_volume_info(JCR *jcr) { return 1;}
684 int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
685 int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
686 int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
687 int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
688 int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
689 int     dir_send_job_status(JCR *jcr) {return 1;}
690
691
692 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
693 {
694    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
695       jcr->VolumeName, dev_name(dev));
696    getchar();   
697    return 1;
698 }