]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bls.c
Simplify bls inner loop
[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 (rec->state & REC_PARTIAL_RECORD) {
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    VolName = Vol;
228    VolName[0] = 0;
229    if (strncmp(infname, "/dev/", 5) != 0) {
230       /* Try stripping file part */
231       p = infname + strlen(infname);
232       while (p >= infname && *p != '/')
233          p--;
234       if (*p == '/') {
235          strcpy(VolName, p+1);
236          *p = 0;
237       }
238    }
239    Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
240    dev = init_dev(NULL, infname);
241    if (!dev) {
242       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
243       exit(1);
244    }
245    /* ***FIXME**** init capabilities */
246    if (!open_device(dev)) {
247       Emsg1(M_FATAL, 0, "Cannot open %s\n", infname);
248       exit(1);
249    }
250    Dmsg0(90, "Device opened for read.\n");
251
252    rec = new_record();
253    block = new_block(dev);
254
255    NumVolumes = 0;
256    CurVolume = 1;
257    for (p = VolName; p && *p; ) {
258       p = strchr(p, '^');
259       if (p) {
260          *p++ = 0;
261       }
262       NumVolumes++;
263    }
264
265    jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
266    strcpy(jcr->VolumeName, VolName);
267    if (!acquire_device_for_read(jcr, dev, block)) {
268       Emsg0(M_ERROR, 0, dev->errmsg);
269       exit(1);
270    }
271 }
272
273 static void do_close()
274 {
275    term_dev(dev);
276    free_record(rec);
277    free_block(block);
278    free_jcr(jcr);
279 }
280
281 static int mount_next_volume(char *infname)
282 {
283    if (rec->remainder) {
284       Dmsg0(20, "Not end of record. Next volume has more data for current record.\n");
285    }
286    Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
287    if (NumVolumes > 1 && CurVolume < NumVolumes) {
288       p = VolName;
289       while (*p++)  
290          { }
291       CurVolume++;
292       Dmsg1(20, "There is another volume %s.\n", p);
293       VolName = p;
294       jcr->VolumeName = check_pool_memory_size(jcr->VolumeName, 
295                           strlen(VolName)+1);
296       strcpy(jcr->VolumeName, VolName);
297
298       close_dev(dev);
299       dev->state &= ~ST_READ; 
300       if (!acquire_device_for_read(jcr, dev, block)) {
301          Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
302          exit(1);
303       }
304       return 1;              /* Next volume mounted */
305    }
306    printf("End of Device reached.\n");
307    return 0;                 /* EOT */
308 }
309
310 /*
311  * Device got an error, attempt to analyse it
312  */
313 static void display_error_status()
314 {
315    uint32_t status;
316
317    Emsg0(M_ERROR, 0, dev->errmsg);
318    status_dev(dev, &status);
319    Dmsg1(20, "Device status: %x\n", status);
320    if (status & MT_EOD)
321       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Data\n");
322    else if (status & MT_EOT)
323       Emsg0(M_ERROR_TERM, 0, "Unexpected End of Tape\n");
324    else if (status & MT_EOF)
325       Emsg0(M_ERROR_TERM, 0, "Unexpected End of File\n");
326    else if (status & MT_DR_OPEN)
327       Emsg0(M_ERROR_TERM, 0, "Tape Door is Open\n");
328    else if (!(status & MT_ONLINE))
329       Emsg0(M_ERROR_TERM, 0, "Unexpected Tape is Off-line\n");
330    else
331       Emsg2(M_ERROR_TERM, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
332 }
333
334
335 /* List just block information */
336 static void do_blocks(char *infname)
337 {
338
339    dump_volume_label(dev);
340
341    /* Assume that we have already read the volume label.
342     * If on second or subsequent volume, adjust buffer pointer 
343     */
344    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
345       Pmsg1(0, "\n\
346 Warning, this Volume is a continuation of Volume %s\n",
347                 dev->VolHdr.PrevVolName);
348    }
349  
350    if (verbose) {
351       rec = new_record();
352    }
353    for ( ;; ) {
354       if (!read_block_from_device(dev, block)) {
355          Dmsg0(20, "!read_block()\n");
356          if (dev->state & ST_EOT) {
357             if (!mount_next_volume(infname)) {
358                break;
359             }
360             continue;
361          }
362          if (dev->state & ST_EOF) {
363             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
364             Dmsg0(20, "read_record got eof. try again\n");
365             continue;
366          }
367          if (dev->state & ST_SHORT) {
368             Emsg0(M_INFO, 0, dev->errmsg);
369             continue;
370          }
371          display_error_status();
372          break;
373       }
374
375       if (verbose) {
376          read_record_from_block(block, rec);
377          Pmsg6(-1, "Block: %d blen=%d First rec FI=%s SessId=%d Strm=%s rlen=%d\n",
378               block->BlockNumber, block->block_len,
379               FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
380               stream_to_ascii(rec->Stream), rec->data_len);
381          rec->remainder = 0;
382       } else {
383          printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
384       }
385
386    }
387    return;
388 }
389
390 /* Do list job records */
391 static void do_jobs(char *infname)
392 {
393
394    /* Assume that we have already read the volume label.
395     * If on second or subsequent volume, adjust buffer pointer 
396     */
397    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
398       Pmsg1(0, "\n\
399 Warning, this Volume is a continuation of Volume %s\n",
400                 dev->VolHdr.PrevVolName);
401    }
402  
403    for ( ;; ) {
404       if (!read_block_from_device(dev, block)) {
405          Dmsg0(20, "!read_block()\n");
406          if (dev->state & ST_EOT) {
407             if (!mount_next_volume(infname)) {
408                break;
409             }
410             continue;
411          }
412          if (dev->state & ST_EOF) {
413             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
414             Dmsg0(20, "read_record got eof. try again\n");
415             continue;
416          }
417          if (dev->state & ST_SHORT) {
418             Pmsg0(000, "Got short block.\n");
419             Emsg0(M_INFO, 0, dev->errmsg);
420             continue;
421          }
422          display_error_status();
423          break;
424       }
425       while (read_record_from_block(block, rec)) {
426          if (debug_level >= 30) {
427             Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
428                   FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
429                   rec->data_len);
430          }
431
432
433          /*  
434           * Check for End of File record (all zeros)
435           *    NOTE: this no longer exists
436           */
437          if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
438             Emsg0(M_ERROR_TERM, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
439          }
440
441          /* 
442           * Check for Start or End of Session Record 
443           *
444           */
445          if (rec->FileIndex < 0) {
446             dump_label_record(dev, rec, verbose);
447             continue;
448          }
449       }
450       rec->remainder = 0;
451    }
452    return;
453 }
454
455 /* Do an ls type listing of an archive */
456 static void do_ls(char *infname)
457 {
458    char fname[2000];
459    struct stat statp;
460    int type;
461    long record_file_index;
462    uint32_t num_files = 0;
463    int record;
464
465    if (dump_label) {
466       dump_volume_label(dev);
467       return;
468    }
469
470    /* Assume that we have already read the volume label.
471     * If on second or subsequent volume, adjust buffer pointer 
472     */
473    if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
474       Pmsg1(0, "\n\
475 Warning, this Volume is a continuation of Volume %s\n",
476                 dev->VolHdr.PrevVolName);
477    }
478  
479    for ( ;; ) {
480       SESSION_LABEL sessrec;
481
482       if (!read_block_from_device(dev, block)) {
483          Dmsg0(20, "!read_record()\n");
484          if (dev->state & ST_EOT) {
485             if (!mount_next_volume(infname)) {
486                break;
487             }
488             continue;
489          }
490          if (dev->state & ST_EOF) {
491             Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
492             Dmsg0(20, "read_record got eof. try again\n");
493             continue;
494          }
495          if (dev->state & ST_SHORT) {
496             Emsg0(M_INFO, 0, dev->errmsg);
497             continue;
498          }
499          display_error_status();
500          break;
501       }
502       if (verbose) {
503          Dmsg2(10, "Block: %d blen=%d\n", block->BlockNumber, block->block_len);
504       }
505
506       record = 0;
507       for (rec->state=0;!(rec->state & REC_BLOCK_EMPTY); ) {
508          if (!read_record_from_block(block, rec)) {
509             if (rec->state & REC_NO_HEADER) {
510                Dmsg2(30, "!read,nohdr-break. stat=%s blk=%d\n", rec_state_to_str(rec), 
511                   block->BlockNumber);
512                break;
513             }
514             if (rec->state & REC_NO_MATCH) {
515                Dmsg2(30, "!read,nomatch-break. stat=%s blk=%d\n", rec_state_to_str(rec), 
516                   block->BlockNumber);
517                break;
518             }
519          }
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 (rec->state & REC_PARTIAL_RECORD) {
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 }