3 * Dumb program to do an "ls" of a Bacula 1.0 mortal file.
7 Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public
20 License along with this program; if not, write to the Free
21 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 #include "findlib/find.h"
30 static void do_blocks(char *infname);
31 static void do_jobs(char *infname);
32 static void do_ls(char *fname);
33 static void print_ls_output(char *fname, char *link, int type, struct stat *statp);
36 static int default_tape = FALSE;
37 static int dump_label = FALSE;
38 static int list_blocks = FALSE;
39 static int list_jobs = FALSE;
40 static int verbose = 0;
42 extern char BaculaId[];
49 "Usage: bls [-d debug_level] <physical-device-name>\n"
51 " -e <file> exclude list\n"
52 " -i <file> include list\n"
54 " -L list tape label\n"
55 " (none of above) list saved files\n"
56 " -t use default tape device\n"
58 " -? print this message\n\n");
63 int main (int argc, char *argv[])
69 my_name_is(argc, argv, "bls");
71 memset(&ff, 0, sizeof(ff));
72 init_include_exclude_files(&ff);
74 while ((ch = getopt(argc, argv, "bd:e:i:jLtv?")) != -1) {
79 case 'd': /* debug level */
80 debug_level = atoi(optarg);
85 case 'e': /* exclude list */
86 if ((fd = fopen(optarg, "r")) == NULL) {
87 Dmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
88 optarg, strerror(errno));
91 while (fgets(line, sizeof(line), fd) != NULL) {
92 strip_trailing_junk(line);
93 Dmsg1(000, "add_exclude %s\n", line);
94 add_fname_to_exclude_list(&ff, line);
99 case 'i': /* include list */
100 if ((fd = fopen(optarg, "r")) == NULL) {
101 Dmsg2(0, "Could not open include file: %s, ERR=%s\n",
102 optarg, strerror(errno));
105 while (fgets(line, sizeof(line), fd) != NULL) {
106 strip_trailing_junk(line);
107 Dmsg1(000, "add_include %s\n", line);
108 add_fname_to_include_list(&ff, 0, line);
138 if (!argc && !default_tape) {
139 Dmsg0(0, "No archive name specified\n");
143 if (ff.included_files_list == NULL) {
144 add_fname_to_include_list(&ff, 0, "/");
148 * Ensure that every message is always printed
150 for (i=1; i<=M_MAX; i++) {
151 add_msg_dest(NULL, MD_STDOUT, i, NULL, NULL);
154 /* Try default device */
156 do_ls(DEFAULT_TAPE_DRIVE);
161 for (i=0; i < argc; i++) {
164 } else if (list_jobs) {
174 static void my_free_jcr(JCR *jcr)
179 /* List just block information */
180 static void do_blocks(char *infname)
187 int NumVolumes, CurVolume;
190 jcr = new_jcr(sizeof(JCR), my_free_jcr);
193 if (strncmp(infname, "/dev/", 5) != 0) {
194 /* Try stripping file part */
195 p = infname + strlen(infname);
196 while (p >= infname && *p != '/')
199 strcpy(VolName, p+1);
203 Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
204 dev = init_dev(NULL, infname);
206 Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
208 /* ***FIXME**** init capabilities */
209 if (!open_device(dev)) {
210 Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
212 Dmsg0(90, "Device opened for read.\n");
215 block = new_block(dev);
219 for (p = VolName; p && *p; ) {
227 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
228 strcpy(jcr->VolumeName, VolName);
229 if (!acquire_device_for_read(jcr, dev, block)) {
230 Emsg0(M_ERROR, 0, dev->errmsg);
234 dump_volume_label(dev);
236 /* Assume that we have already read the volume label.
237 * If on second or subsequent volume, adjust buffer pointer
239 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
241 Warning, this Volume is a continuation of Volume %s\n",
242 dev->VolHdr.PrevVolName);
247 if (!read_block_from_device(dev, block)) {
249 Dmsg0(20, "!read_record()\n");
250 if (dev->state & ST_EOT) {
251 if (rec->remainder) {
252 Dmsg0(20, "Not end of record.\n");
254 Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
255 if (NumVolumes > 1 && CurVolume < NumVolumes) {
260 Dmsg1(20, "There is another volume %s.\n", p);
263 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName,
265 strcpy(jcr->VolumeName, VolName);
266 printf("Mount Volume %s on device %s and press return when ready.",
269 block->binbuf = 0; /* consumed all bytes */
270 if (!ready_dev_for_read(jcr, dev, block)) {
271 Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
275 printf("End of Device reached.\n");
278 if (dev->state & ST_EOF) {
279 Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
280 Dmsg0(20, "read_record got eof. try again\n");
283 if (dev->state & ST_SHORT) {
284 Emsg0(M_INFO, 0, dev->errmsg);
287 Emsg0(M_ERROR, 0, dev->errmsg);
288 status_dev(dev, &status);
289 Dmsg1(20, "Device status: %x\n", status);
291 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
292 else if (status & MT_EOT)
293 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
294 else if (status & MT_EOF)
295 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
296 else if (status & MT_DR_OPEN)
297 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
298 else if (!(status & MT_ONLINE))
299 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
301 Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
305 printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
315 /* Do list job records */
316 static void do_jobs(char *infname)
323 int NumVolumes, CurVolume;
326 jcr = new_jcr(sizeof(JCR), my_free_jcr);
329 if (strncmp(infname, "/dev/", 5) != 0) {
330 /* Try stripping file part */
331 p = infname + strlen(infname);
332 while (p >= infname && *p != '/')
335 strcpy(VolName, p+1);
339 Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
340 dev = init_dev(NULL, infname);
342 Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
344 /* ***FIXME**** init capabilities */
345 if (!open_device(dev)) {
346 Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
348 Dmsg0(90, "Device opened for read.\n");
351 block = new_block(dev);
355 for (p = VolName; p && *p; ) {
363 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
364 strcpy(jcr->VolumeName, VolName);
365 if (!acquire_device_for_read(jcr, dev, block)) {
366 Emsg0(M_ERROR, 0, dev->errmsg);
370 /* Assume that we have already read the volume label.
371 * If on second or subsequent volume, adjust buffer pointer
373 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
375 Warning, this Volume is a continuation of Volume %s\n",
376 dev->VolHdr.PrevVolName);
382 if (!read_record(dev, block, rec)) {
384 Dmsg0(20, "!read_record()\n");
385 if (dev->state & ST_EOT) {
386 if (rec->remainder) {
387 Dmsg0(20, "Not end of record.\n");
389 Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
390 if (NumVolumes > 1 && CurVolume < NumVolumes) {
395 Dmsg1(20, "There is another volume %s.\n", p);
398 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName,
400 strcpy(jcr->VolumeName, VolName);
401 printf("Mount Volume %s on device %s and press return when ready.",
404 if (!ready_dev_for_read(jcr, dev, block)) {
405 Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
407 record = new_record();
408 read_record(dev, block, record); /* read vol label */
409 dump_label_record(dev, record, verbose);
413 printf("End of Device reached.\n");
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");
421 if (dev->state & ST_SHORT) {
422 Emsg0(M_INFO, 0, dev->errmsg);
425 Emsg0(M_ERROR, 0, dev->errmsg);
426 status_dev(dev, &status);
427 Dmsg1(20, "Device status: %x\n", status);
429 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
430 else if (status & MT_EOT)
431 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
432 else if (status & MT_EOF)
433 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
434 else if (status & MT_DR_OPEN)
435 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
436 else if (!(status & MT_ONLINE))
437 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
439 Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
443 if (debug_level >= 30) {
444 Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
445 FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream),
451 * Check for End of File record (all zeros)
452 * NOTE: this no longer exists
454 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
455 Emsg0(M_ABORT, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
459 * Check for Start or End of Session Record
462 if (rec->FileIndex < 0) {
463 dump_label_record(dev, rec, verbose);
474 /* Do an ls type listing of an archive */
475 static void do_ls(char *infname)
483 long record_file_index;
486 int NumVolumes, CurVolume;
489 jcr = new_jcr(sizeof(JCR), my_free_jcr);
492 if (strncmp(infname, "/dev/", 5) != 0) {
493 /* Try stripping file part */
494 p = infname + strlen(infname);
495 while (p >= infname && *p != '/')
498 strcpy(VolName, p+1);
502 Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
503 dev = init_dev(NULL, infname);
505 Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
507 /* ***FIXME**** init capabilities */
508 if (!open_device(dev)) {
509 Emsg1(M_ERROR, 0, "Cannot open %s\n", infname);
512 Dmsg0(90, "Device opened for read.\n");
515 block = new_block(dev);
519 for (p = VolName; p && *p; ) {
527 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
528 strcpy(jcr->VolumeName, VolName);
529 if (!acquire_device_for_read(jcr, dev, block)) {
530 Emsg0(M_ERROR, 0, dev->errmsg);
535 dump_volume_label(dev);
542 /* Assume that we have already read the volume label.
543 * If on second or subsequent volume, adjust buffer pointer
545 if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
547 Warning, this Volume is a continuation of Volume %s\n",
548 dev->VolHdr.PrevVolName);
554 if (!read_record(dev, block, rec)) {
556 Dmsg0(20, "!read_record()\n");
557 if (dev->state & ST_EOT) {
558 if (rec->remainder) {
559 Dmsg0(20, "Not end of record.\n");
561 Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
562 if (NumVolumes > 1 && CurVolume < NumVolumes) {
567 Dmsg1(20, "There is another volume %s.\n", p);
570 jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName,
572 strcpy(jcr->VolumeName, VolName);
573 printf("Mount Volume %s on device %s and press return when ready.",
576 if (!ready_dev_for_read(jcr, dev, block)) {
577 Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
579 record = new_record();
580 read_record(dev, block, record); /* read vol label */
581 dump_label_record(dev, record, 0);
585 printf("End of Device reached.\n");
588 if (dev->state & ST_EOF) {
589 Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
590 Dmsg0(20, "read_record got eof. try again\n");
593 if (dev->state & ST_SHORT) {
594 Emsg0(M_INFO, 0, dev->errmsg);
597 Emsg0(M_ERROR, 0, dev->errmsg);
598 status_dev(dev, &status);
599 Dmsg1(20, "Device status: %x\n", status);
601 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
602 else if (status & MT_EOT)
603 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
604 else if (status & MT_EOF)
605 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
606 else if (status & MT_DR_OPEN)
607 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
608 else if (!(status & MT_ONLINE))
609 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
611 Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
615 if (debug_level >= 30) {
616 Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
617 FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream),
623 * Check for End of File record (all zeros)
624 * NOTE: this no longer exists
626 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
627 Emsg0(M_ABORT, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
631 * Check for Start or End of Session Record
634 if (rec->FileIndex < 0) {
635 dump_label_record(dev, rec, 0);
639 /* File Attributes stream */
640 if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
642 sscanf(rec->data, "%ld %d %s", &record_file_index, &type, fname);
643 if (record_file_index != rec->FileIndex) {
644 Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
645 rec->FileIndex, record_file_index);
648 /* Skip to attributes */
651 decode_stat(ap, &statp);
652 /* Skip to link name */
655 print_ls_output(fname, ap, type, &statp);
665 extern char *getuser(uid_t uid);
666 extern char *getgroup(gid_t gid);
668 static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
674 if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
677 p = encode_mode(statp->st_mode, buf);
678 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
680 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
682 n = sprintf(p, "%8" lld " ", (uint64_t)statp->st_size);
684 p = encode_time(statp->st_ctime, p);
690 if (type == FT_DIR) {
693 if (type == FT_LNK) {