]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/bls.c
This commit was manufactured by cvs2svn to create tag
[bacula/bacula] / bacula / src / stored / bls.c
index de60d1af7f8b9b52957011071e469d5251803562..8dbea4c8c2d4ae53d93922394d67c7da381f8a00 100644 (file)
@@ -2,24 +2,20 @@
  *
  *  Dumb program to do an "ls" of a Bacula 1.0 mortal file.
  *
+ *   Version $Id$
  */
 /*
-   Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+   Copyright (C) 2000-2006 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of
-   the License, or (at your option) any later version.
+   modify it under the terms of the GNU General Public License
+   version 2 as amended with additional clauses defined in the
+   file LICENSE in the main source directory.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public
-   License along with this program; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
+   the file LICENSE for additional details.
 
  */
 
 #include "stored.h"
 #include "findlib/find.h"
 
+#if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
+int win32_client = 1;
+#else
+int win32_client = 0;
+#endif
+
+/* Dummy functions */
+int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
+
 static void do_blocks(char *infname);
 static void do_jobs(char *infname);
 static void do_ls(char *fname);
-static void print_ls_output(char *fname, char *link, int type, struct stat *statp);
+static void do_close(JCR *jcr);
+static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
+static bool record_cb(DCR *dcr, DEV_RECORD *rec);
 
 static DEVICE *dev;
-static int default_tape = FALSE;
-static int dump_label = FALSE;
-static int list_blocks = FALSE;
-static int list_jobs = FALSE;
-static int verbose = 0;
+static DCR *dcr;
+static bool dump_label = false;
+static bool list_blocks = false;
+static bool list_jobs = false;
+static DEV_RECORD *rec;
+static JCR *jcr;
+static SESSION_LABEL sessrec;
+static uint32_t num_files = 0;
+static ATTR *attr;
+
+#define CONFIG_FILE "bacula-sd.conf"
+char *configfile = NULL;
+STORES *me = NULL;                    /* our Global resource */
+bool forge_on = false;
+pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
 
-extern char BaculaId[];
 
-static FF_PKT ff;
+static FF_PKT *ff;
+
+static BSR *bsr = NULL;
 
 static void usage()
 {
-   fprintf(stderr,
-"Usage: bls [-d debug_level] <physical-device-name>\n"
-"       -b              list blocks\n"
+   fprintf(stderr, _(
+"Copyright (C) 2000-2005 Kern Sibbald.\n"
+"\nVersion: %s (%s)\n\n"
+"Usage: bls [options] <device-name>\n"
+"       -b <file>       specify a bootstrap file\n"
+"       -c <file>       specify a config file\n"
+"       -d <level>      specify debug level\n"
 "       -e <file>       exclude list\n"
 "       -i <file>       include list\n"
 "       -j              list jobs\n"
-"       -L              list tape label\n"
-"    (none of above)    list saved files\n"
-"       -t              use default tape device\n"
+"       -k              list blocks\n"
+"    (no j or k option) list saved files\n"
+"       -L              dump label\n"
+"       -p              proceed inspite of errors\n"
 "       -v              be verbose\n"
-"       -?              print this message\n\n");
+"       -V              specify Volume names (separated by |)\n"
+"       -?              print this message\n\n"), VERSION, BDATE);
    exit(1);
 }
 
@@ -65,641 +90,355 @@ int main (int argc, char *argv[])
    int i, ch;
    FILE *fd;
    char line[1000];
+   char *VolumeName= NULL;
+   char *bsrName = NULL;
+   bool ignore_label_errors = false;
+
+   setlocale(LC_ALL, "");
+   bindtextdomain("bacula", LOCALEDIR);
+   textdomain("bacula");
 
+   working_directory = "/tmp";
    my_name_is(argc, argv, "bls");
+   init_msg(NULL, NULL);              /* initialize message handler */
 
-   memset(&ff, 0, sizeof(ff));
-   init_include_exclude_files(&ff);
+   ff = init_find_files();
 
-   while ((ch = getopt(argc, argv, "bd:e:i:jLtv?")) != -1) {
+   while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLpvV:?")) != -1) {
       switch (ch) {
-         case 'b':
-           list_blocks = TRUE;
-           break;
-         case 'd':                    /* debug level */
-           debug_level = atoi(optarg);
-           if (debug_level <= 0)
-              debug_level = 1; 
-           break;
-
-         case 'e':                    /* exclude list */
-            if ((fd = fopen(optarg, "r")) == NULL) {
-               Dmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
-                 optarg, strerror(errno));
-              exit(1);
-           }
-           while (fgets(line, sizeof(line), fd) != NULL) {
-              strip_trailing_junk(line);
-               Dmsg1(000, "add_exclude %s\n", line);
-              add_fname_to_exclude_list(&ff, line);
-           }
-           fclose(fd);
-           break;
-
-         case 'i':                    /* include list */
-            if ((fd = fopen(optarg, "r")) == NULL) {
-               Dmsg2(0, "Could not open include file: %s, ERR=%s\n",
-                 optarg, strerror(errno));
-              exit(1);
-           }
-           while (fgets(line, sizeof(line), fd) != NULL) {
-              strip_trailing_junk(line);
-               Dmsg1(000, "add_include %s\n", line);
-              add_fname_to_include_list(&ff, 0, line);
-           }
-           fclose(fd);
-           break;
-
-         case 'j':
-           list_jobs = TRUE;
-           break;
-
-         case 'L':
-           dump_label = TRUE;
-           break;
-
-         case 't':
-           default_tape = TRUE;
-           break;
-
-         case 'v':
-           verbose++;
-           break;
-
-         case '?':
-        default:
-           usage();
-
-      }  
-   }
+      case 'b':
+         bsrName = optarg;
+         break;
+
+      case 'c':                    /* specify config file */
+         if (configfile != NULL) {
+            free(configfile);
+         }
+         configfile = bstrdup(optarg);
+         break;
+
+      case 'd':                    /* debug level */
+         debug_level = atoi(optarg);
+         if (debug_level <= 0)
+            debug_level = 1;
+         break;
+
+      case 'e':                    /* exclude list */
+         if ((fd = fopen(optarg, "r")) == NULL) {
+            Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
+            Dmsg1(100, "add_exclude %s\n", line);
+            add_fname_to_exclude_list(ff, line);
+         }
+         fclose(fd);
+         break;
+
+      case 'i':                    /* include list */
+         if ((fd = fopen(optarg, "r")) == NULL) {
+            Pmsg2(0, _("Could not open include file: %s, ERR=%s\n"),
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
+            Dmsg1(100, "add_include %s\n", line);
+            add_fname_to_include_list(ff, 0, line);
+         }
+         fclose(fd);
+         break;
+
+      case 'j':
+         list_jobs = true;
+         break;
+
+      case 'k':
+         list_blocks = true;
+         break;
+
+      case 'L':
+         dump_label = true;
+         break;
+
+      case 'p':
+         ignore_label_errors = true;
+         forge_on = true;
+         break;
+
+      case 'v':
+         verbose++;
+         break;
+
+      case 'V':                    /* Volume name */
+         VolumeName = optarg;
+         break;
+
+      case '?':
+      default:
+         usage();
+
+      } /* end switch */
+   } /* end while */
    argc -= optind;
    argv += optind;
 
-   if (!argc && !default_tape) {
-      Dmsg0(0, "No archive name specified\n");
+   if (!argc) {
+      Pmsg0(0, _("No archive name specified\n"));
       usage();
    }
 
-   if (ff.included_files_list == NULL) {
-      add_fname_to_include_list(&ff, 0, "/");
+   if (configfile == NULL) {
+      configfile = bstrdup(CONFIG_FILE);
    }
 
-   /*
-    * Ensure that every message is always printed
-    */
-   for (i=1; i<=M_MAX; i++) {
-      add_msg_dest(MD_STDOUT, i, NULL, NULL);
-   }
+   parse_config(configfile);
 
-   /* Try default device */
-   if (default_tape) {
-      do_ls(DEFAULT_TAPE_DRIVE);
-      return 0;
+   if (ff->included_files_list == NULL) {
+      add_fname_to_include_list(ff, 0, "/");
    }
 
-
    for (i=0; i < argc; i++) {
+      if (bsrName) {
+         bsr = parse_bsr(NULL, bsrName);
+      }
+      jcr = setup_jcr("bls", argv[i], bsr, VolumeName, 1); /* acquire for read */
+      if (!jcr) {
+         exit(1);
+      }
+      jcr->ignore_label_errors = ignore_label_errors;
+      dev = jcr->dcr->dev;
+      if (!dev) {
+         exit(1);
+      }
+      dcr = jcr->dcr;
+      rec = new_record();
+      attr = new_attr();
+      /*
+       * Assume that we have already read the volume label.
+       * If on second or subsequent volume, adjust buffer pointer
+       */
+      if (dev->VolHdr.PrevVolumeName[0] != 0) { /* second volume */
+         Pmsg1(0, _("\n"
+                    "Warning, this Volume is a continuation of Volume %s\n"),
+                dev->VolHdr.PrevVolumeName);
+      }
+
       if (list_blocks) {
-        do_blocks(argv[i]);
+         do_blocks(argv[i]);
       } else if (list_jobs) {
-        do_jobs(argv[i]);
+         do_jobs(argv[i]);
       } else {
-        do_ls(argv[i]);
+         do_ls(argv[i]);
       }
+      do_close(jcr);
    }
-
+   if (bsr) {
+      free_bsr(bsr);
+   }
+   term_include_exclude_files(ff);
+   term_find_files(ff);
    return 0;
 }
 
-static void my_free_jcr(JCR *jcr)
+
+static void do_close(JCR *jcr)
 {
-   return;
+   release_device(jcr->dcr);
+   free_attr(attr);
+   free_record(rec);
+   free_jcr(jcr);
+   term_dev(dev);
 }
 
+
 /* List just block information */
 static void do_blocks(char *infname)
 {
-   char Vol[2000];
-   char *VolName;
-   char *p;
-   DEV_RECORD *rec;
-   DEV_BLOCK *block;
-   int NumVolumes, CurVolume;
-   JCR *jcr;
-
-   jcr = new_jcr(sizeof(JCR), my_free_jcr);
-   VolName = Vol;
-   VolName[0] = 0;
-   if (strncmp(infname, "/dev/", 5) != 0) {
-      /* Try stripping file part */
-      p = infname + strlen(infname);
-      while (p >= infname && *p != '/')
-        p--;
-      if (*p == '/') {
-        strcpy(VolName, p+1);
-        *p = 0;
-      }
-   }
-   Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
-   dev = init_dev(NULL, infname);
-   if (!dev) {
-      Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
-   }
-   /* ***FIXME**** init capabilities */
-   if (!open_device(dev)) {
-      Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
-   }
-   Dmsg0(90, "Device opened for read.\n");
-
-   rec = new_record();
-   block = new_block(dev);
-
-   NumVolumes = 0;
-   CurVolume = 1;
-   for (p = VolName; p && *p; ) {
-      p = strchr(p, '^');
-      if (p) {
-        *p++ = 0;
-      }
-      NumVolumes++;
-   }
-
-   jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
-   strcpy(jcr->VolumeName, VolName);
-   if (!acquire_device_for_read(jcr, dev, block)) {
-      Emsg0(M_ERROR, 0, dev->errmsg);
-      exit(1);
-   }
-
-   dump_volume_label(dev);
-
-   /* Assume that we have already read the volume label.
-    * If on second or subsequent volume, adjust buffer pointer 
-    */
-   if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
-      Dmsg1(0, "\n\
-Warning, this Volume is a continuation of Volume %s\n",
-               dev->VolHdr.PrevVolName);
-   }
+   char buf1[100], buf2[100];
+   DEV_BLOCK *block = dcr->block;
    for ( ;; ) {
-
-      if (!read_block_from_device(dev, block)) {
-        uint32_t status;
-         Dmsg0(20, "!read_record()\n");
-        if (dev->state & ST_EOT) {
-           if (rec->remainder) {
-               Dmsg0(20, "Not end of record.\n");
-           }
-            Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
-           if (NumVolumes > 1 && CurVolume < NumVolumes) {
-              p = VolName;
-              while (*p++)  
-                 { }
-              CurVolume++;
-               Dmsg1(20, "There is another volume %s.\n", p);
-              VolName = p;
-              close_dev(dev);
-              jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, 
-                                  strlen(VolName)+1);
-              strcpy(jcr->VolumeName, VolName);
-               printf("Mount Volume %s on device %s and press return when ready.",
-                 VolName, infname);
-              getchar();   
-              block->binbuf = 0;     /* consumed all bytes */
-              if (!ready_dev_for_read(jcr, dev, block)) {
-                  Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
-              }
-              continue;
-           }
-            printf("End of Device reached.\n");
-           break;
-        }
-        if (dev->state & ST_EOF) {
-            Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
+      if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
+         Dmsg1(100, "!read_block(): ERR=%s\n", dev->strerror());
+         if (dev->at_eot()) {
+            if (!mount_next_read_volume(dcr)) {
+               Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
+                  dev->file, dev->print_name(), dcr->VolumeName);
+               break;
+            }
+            /* Read and discard Volume label */
+            DEV_RECORD *record;
+            record = new_record();
+            read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK);
+            read_record_from_block(block, record);
+            get_session_record(dev, record, &sessrec);
+            free_record(record);
+            Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
+         } else if (dev->at_eof()) {
+            Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
+               dev->file, dev->print_name(), dcr->VolumeName);
             Dmsg0(20, "read_record got eof. try again\n");
-           continue;
-        }
-        if (dev->state & ST_SHORT) {
-           Emsg0(M_INFO, 0, dev->errmsg);
-           continue;
-        }
-        Emsg0(M_ERROR, 0, dev->errmsg);
-        status_dev(dev, &status);
-         Dmsg1(20, "Device status: %x\n", status);
-        if (status & MT_EOD)
-            Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
-        else if (status & MT_EOT)
-            Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
-        else if (status & MT_EOF)
-            Emsg0(M_ABORT, 0, "Unexpected End of File\n");
-        else if (status & MT_DR_OPEN)
-            Emsg0(M_ABORT, 0, "Tape Door is Open\n");
-        else if (!(status & MT_ONLINE))
-            Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
-        else
-            Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
-        break;
+            continue;
+         } else if (dev->is_short_block()) {
+            Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
+            continue;
+         } else {
+            /* I/O error */
+            display_tape_error_status(jcr, dev);
+            break;
+         }
+      }
+      if (!match_bsr_block(bsr, block)) {
+         Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
+            block->BlockNumber, block->block_len, block->BlockVer,
+            block->VolSessionId, block->VolSessionTime);
+         continue;
+      }
+      Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
+        block->BlockNumber, block->block_len, block->BlockVer,
+        block->VolSessionId, block->VolSessionTime);
+      if (verbose == 1) {
+         read_record_from_block(block, rec);
+         Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
+              dev->file, dev->block_num,
+              block->BlockNumber, block->block_len,
+              FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
+              stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
+         rec->remainder = 0;
+      } else if (verbose > 1) {
+         dump_block(block, "");
+      } else {
+         printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
       }
-
-      printf("Block: %d size=%d\n", block->BlockNumber, block->block_len);
 
    }
-   term_dev(dev);
-   free_record(rec);
-   free_block(block);
-   free_jcr(jcr);
    return;
 }
 
-/* Do list job records */
-static void do_jobs(char *infname)
+/*
+ * We are only looking for labels or in particular Job Session records
+ */
+static bool jobs_cb(DCR *dcr, DEV_RECORD *rec)
 {
-   char Vol[2000];
-   char *VolName;
-   char *p;
-   DEV_RECORD *rec;
-   DEV_BLOCK *block;
-   int NumVolumes, CurVolume;
-   JCR *jcr;
-
-   jcr = new_jcr(sizeof(JCR), my_free_jcr);
-   VolName = Vol;
-   VolName[0] = 0;
-   if (strncmp(infname, "/dev/", 5) != 0) {
-      /* Try stripping file part */
-      p = infname + strlen(infname);
-      while (p >= infname && *p != '/')
-        p--;
-      if (*p == '/') {
-        strcpy(VolName, p+1);
-        *p = 0;
-      }
-   }
-   Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
-   dev = init_dev(NULL, infname);
-   if (!dev) {
-      Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
-   }
-   /* ***FIXME**** init capabilities */
-   if (!open_device(dev)) {
-      Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
-   }
-   Dmsg0(90, "Device opened for read.\n");
-
-   rec = new_record();
-   block = new_block(dev);
-
-   NumVolumes = 0;
-   CurVolume = 1;
-   for (p = VolName; p && *p; ) {
-      p = strchr(p, '^');
-      if (p) {
-        *p++ = 0;
-      }
-      NumVolumes++;
-   }
-
-   jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
-   strcpy(jcr->VolumeName, VolName);
-   if (!acquire_device_for_read(jcr, dev, block)) {
-      Emsg0(M_ERROR, 0, dev->errmsg);
-      exit(1);
+   if (rec->FileIndex < 0) {
+      dump_label_record(dcr->dev, rec, verbose);
    }
+   rec->remainder = 0;
+   return true;
+}
 
-   /* Assume that we have already read the volume label.
-    * If on second or subsequent volume, adjust buffer pointer 
-    */
-   if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
-      Dmsg1(0, "\n\
-Warning, this Volume is a continuation of Volume %s\n",
-               dev->VolHdr.PrevVolName);
-   }
-   for ( ;; ) {
-      DEV_RECORD *record;
-
-      if (!read_record(dev, block, rec)) {
-        uint32_t status;
-         Dmsg0(20, "!read_record()\n");
-        if (dev->state & ST_EOT) {
-           if (rec->remainder) {
-               Dmsg0(20, "Not end of record.\n");
-           }
-            Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
-           if (NumVolumes > 1 && CurVolume < NumVolumes) {
-              p = VolName;
-              while (*p++)  
-                 { }
-              CurVolume++;
-               Dmsg1(20, "There is another volume %s.\n", p);
-              VolName = p;
-              close_dev(dev);
-              jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, 
-                    strlen(VolName)+1);
-              strcpy(jcr->VolumeName, VolName);
-               printf("Mount Volume %s on device %s and press return when ready.",
-                 VolName, infname);
-              getchar();   
-              if (!ready_dev_for_read(jcr, dev, block)) {
-                  Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
-              }
-              record = new_record();
-              read_record(dev, block, record); /* read vol label */
-              dump_label_record(dev, record, verbose);
-              free_record(record);
-              continue;
-           }
-            printf("End of Device reached.\n");
-           break;
-        }
-        if (dev->state & ST_EOF) {
-            Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
-            Dmsg0(20, "read_record got eof. try again\n");
-           continue;
-        }
-        if (dev->state & ST_SHORT) {
-           Emsg0(M_INFO, 0, dev->errmsg);
-           continue;
-        }
-        Emsg0(M_ERROR, 0, dev->errmsg);
-        status_dev(dev, &status);
-         Dmsg1(20, "Device status: %x\n", status);
-        if (status & MT_EOD)
-            Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
-        else if (status & MT_EOT)
-            Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
-        else if (status & MT_EOF)
-            Emsg0(M_ABORT, 0, "Unexpected End of File\n");
-        else if (status & MT_DR_OPEN)
-            Emsg0(M_ABORT, 0, "Tape Door is Open\n");
-        else if (!(status & MT_ONLINE))
-            Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
-        else
-            Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
-        break;
-      }
-
-      if (debug_level >= 30) {
-         Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
-              FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
-              rec->data_len);
-      }
-
-
-      /*  
-       * Check for End of File record (all zeros)
-       *    NOTE: this no longer exists
-       */
-      if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
-         Emsg0(M_ABORT, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
-      }
-
-      /* 
-       * Check for Start or End of Session Record 
-       *
-       */
-      if (rec->FileIndex < 0) {
-        dump_label_record(dev, rec, verbose);
-        continue;
-      }
-   }
-   term_dev(dev);
-   free_record(rec);
-   free_block(block);
-   free_jcr(jcr);
-   return;
+/* Do list job records */
+static void do_jobs(char *infname)
+{
+   read_records(dcr, jobs_cb, mount_next_read_volume);
 }
 
 /* Do an ls type listing of an archive */
 static void do_ls(char *infname)
 {
-   char fname[1000];
-   char Vol[2000];
-   char *VolName;
-   char *p;
-   struct stat statp;
-   int type;
-   long record_file_index;
-   DEV_RECORD *rec;
-   DEV_BLOCK *block;
-   int NumVolumes, CurVolume;
-   JCR *jcr;
-
-   jcr = new_jcr(sizeof(JCR), my_free_jcr);
-   VolName = Vol;
-   VolName[0] = 0;
-   if (strncmp(infname, "/dev/", 5) != 0) {
-      /* Try stripping file part */
-      p = infname + strlen(infname);
-      while (p >= infname && *p != '/')
-        p--;
-      if (*p == '/') {
-        strcpy(VolName, p+1);
-        *p = 0;
-      }
-   }
-   Dmsg2(10, "Device=%s, Vol=%s.\n", infname, VolName);
-   dev = init_dev(NULL, infname);
-   if (!dev) {
-      Emsg1(M_ABORT, 0, "Cannot open %s\n", infname);
-   }
-   /* ***FIXME**** init capabilities */
-   if (!open_device(dev)) {
-      Emsg1(M_ERROR, 0, "Cannot open %s\n", infname);
-      exit(1);
-   }
-   Dmsg0(90, "Device opened for read.\n");
-
-   rec = new_record();
-   block = new_block(dev);
-
-   NumVolumes = 0;
-   CurVolume = 1;
-   for (p = VolName; p && *p; ) {
-      p = strchr(p, '^');
-      if (p) {
-        *p++ = 0;
-      }
-      NumVolumes++;
-   }
-
-   jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
-   strcpy(jcr->VolumeName, VolName);
-   if (!acquire_device_for_read(jcr, dev, block)) {
-      Emsg0(M_ERROR, 0, dev->errmsg);
-      exit(1);
-   }
-
    if (dump_label) {
       dump_volume_label(dev);
-      term_dev(dev);
-      free_record(rec);
-      free_block(block);
       return;
    }
+   read_records(dcr, record_cb, mount_next_read_volume);
+   printf("%u files found.\n", num_files);
+}
 
-   /* Assume that we have already read the volume label.
-    * If on second or subsequent volume, adjust buffer pointer 
-    */
-   if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
-      Dmsg1(0, "\n\
-Warning, this Volume is a continuation of Volume %s\n",
-               dev->VolHdr.PrevVolName);
+/*
+ * Called here for each record from read_records()
+ */
+static bool record_cb(DCR *dcr, DEV_RECORD *rec)
+{
+   if (rec->FileIndex < 0) {
+      get_session_record(dev, rec, &sessrec);
+      return true;
    }
-   for ( ;; ) {
-      DEV_RECORD *record;
-
-      if (!read_record(dev, block, rec)) {
-        uint32_t status;
-         Dmsg0(20, "!read_record()\n");
-        if (dev->state & ST_EOT) {
-           if (rec->remainder) {
-               Dmsg0(20, "Not end of record.\n");
-           }
-            Dmsg2(20, "NumVolumes=%d CurVolume=%d\n", NumVolumes, CurVolume);
-           if (NumVolumes > 1 && CurVolume < NumVolumes) {
-              p = VolName;
-              while (*p++)  
-                 { }
-              CurVolume++;
-               Dmsg1(20, "There is another volume %s.\n", p);
-              VolName = p;
-              close_dev(dev);
-              jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, 
-                   strlen(VolName)+1);
-              strcpy(jcr->VolumeName, VolName);
-               printf("Mount Volume %s on device %s and press return when ready.",
-                 VolName, infname);
-              getchar();   
-              if (!ready_dev_for_read(jcr, dev, block)) {
-                  Emsg2(M_ABORT, 0, "Cannot open Dev=%s, Vol=%s\n", infname, VolName);
-              }
-              record = new_record();
-              read_record(dev, block, record); /* read vol label */
-              dump_label_record(dev, record, 0);
-              free_record(record);
-              continue;
-           }
-            printf("End of Device reached.\n");
-           break;
-        }
-        if (dev->state & ST_EOF) {
-            Emsg1(M_INFO, 0, "Got EOF on device %s\n", dev_name(dev));
-            Dmsg0(20, "read_record got eof. try again\n");
-           continue;
-        }
-        if (dev->state & ST_SHORT) {
-           Emsg0(M_INFO, 0, dev->errmsg);
-           continue;
-        }
-        Emsg0(M_ERROR, 0, dev->errmsg);
-        status_dev(dev, &status);
-         Dmsg1(20, "Device status: %x\n", status);
-        if (status & MT_EOD)
-            Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
-        else if (status & MT_EOT)
-            Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
-        else if (status & MT_EOF)
-            Emsg0(M_ABORT, 0, "Unexpected End of File\n");
-        else if (status & MT_DR_OPEN)
-            Emsg0(M_ABORT, 0, "Tape Door is Open\n");
-        else if (!(status & MT_ONLINE))
-            Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
-        else
-            Emsg2(M_ABORT, 0, "Read error on Record Header %s: %s\n", dev_name(dev), strerror(errno));
-        break;
+   /* File Attributes stream */
+   if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
+       rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
+
+      if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
+         if (!forge_on) {
+            Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
+         }
+         num_files++;
+         return true;
       }
 
-      if (debug_level >= 30) {
-         Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
-              FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream), 
-              rec->data_len);
-      }
-
-
-      /*  
-       * Check for End of File record (all zeros)
-       *    NOTE: this no longer exists
-       */
-      if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
-         Emsg0(M_ABORT, 0, "Zero VolSessionId and VolSessionTime. This shouldn't happen\n");
+      if (attr->file_index != rec->FileIndex) {
+         Emsg2(forge_on?M_WARNING:M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
+               rec->FileIndex, attr->file_index);
       }
 
-      /* 
-       * Check for Start or End of Session Record 
-       *
-       */
-      if (rec->FileIndex < 0) {
-        dump_label_record(dev, rec, 0);
-        continue;
-      }
+      attr->data_stream = decode_stat(attr->attr, &attr->statp, &attr->LinkFI);
+      build_attr_output_fnames(jcr, attr);
 
-      /* File Attributes stream */
-      if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
-        char *ap;
-         sscanf(rec->data, "%ld %d %s", &record_file_index, &type, fname);
-        if (record_file_index != rec->FileIndex) {
-            Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
-              rec->FileIndex, record_file_index);
-        }
-        ap = rec->data;
-        /* Skip to attributes */
-        while (*ap++ != 0)
-           ;
-        decode_stat(ap, &statp);
-        /* Skip to link name */  
-        while (*ap++ != 0)
-           ;
-        print_ls_output(fname, ap, type, &statp);
+      if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
+         if (verbose) {
+            Pmsg5(-1, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
+                  rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+         }
+         print_ls_output(jcr, attr);
+         num_files++;
       }
    }
-   term_dev(dev);
-   free_record(rec);
-   free_block(block);
-   free_jcr(jcr);
-   return;
+   return true;
 }
 
-extern char *getuser(uid_t uid);
-extern char *getgroup(gid_t gid);
 
-static void print_ls_output(char *fname, char *link, int type, struct stat *statp)
+static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
 {
-   char buf[1000]; 
-   char *p, *f;
-   int n;
-
-   if (!file_is_included(&ff, fname) || file_is_excluded(&ff, fname)) {
-      return;
-   }
-   p = encode_mode(statp->st_mode, buf);
-   n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
-   p += n;
-   n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
-   p += n;
-   n = sprintf(p, "%8" lld " ", (uint64_t)statp->st_size);
-   p += n;
-   p = encode_time(statp->st_ctime, p);
-   *p++ = ' ';
-   *p++ = ' ';
-   /* Copy file name */
-   for (f=fname; *f; )
-      *p++ = *f++;
-   if (type == FT_DIR) {
-      *p++ = '/';
+   const char *rtype;
+   memset(sessrec, 0, sizeof(sessrec));
+   switch (rec->FileIndex) {
+   case PRE_LABEL:
+      rtype = _("Fresh Volume Label");
+      break;
+   case VOL_LABEL:
+      rtype = _("Volume Label");
+      unser_volume_label(dev, rec);
+      break;
+   case SOS_LABEL:
+      rtype = _("Begin Job Session");
+      unser_session_label(sessrec, rec);
+      break;
+   case EOS_LABEL:
+      rtype = _("End Job Session");
+      break;
+   case EOM_LABEL:
+      rtype = _("End of Medium");
+      break;
+   default:
+      rtype = _("Unknown");
+      break;
    }
-   if (type == FT_LNK) {
-      *p++ = ' ';
-      *p++ = '-';
-      *p++ = '>';
-      *p++ = ' ';
-      /* Copy link name */
-      for (f=link; *f; )
-        *p++ = *f++;
+   Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
+         rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+   if (verbose) {
+      Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
+            rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
    }
-   *p++ = '\n';
-   *p = 0;
-   fputs(buf, stdout);
+}
+
+
+/* Dummies to replace askdir.c */
+bool    dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing) { return 1;}
+bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
+bool    dir_update_volume_info(DCR *dcr, bool relabel) { return 1; }
+bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
+bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
+bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
+bool    dir_send_job_status(JCR *jcr) {return 1;}
+int     generate_job_event(JCR *jcr, const char *event) { return 1; }
+       
+
+bool dir_ask_sysop_to_mount_volume(DCR *dcr)
+{
+   DEVICE *dev = dcr->dev;
+   fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
+      dcr->VolumeName, dev->print_name());
+   getchar();
+   return true;
 }