]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/btape.c
ebl First cup of faketape dummy tape driver for regression testing.
[bacula/bacula] / bacula / src / stored / btape.c
index 59577d8b060d7a702bae5ed1a20fe284a1d2c67a..a92d01e215129b6e8e775def821fd838a361fc95 100644 (file)
@@ -1,3 +1,30 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of John Walker.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
 /*
  *
  *   Bacula Tape manipulation program
  *   Version $Id$
  *
  */
-/*
-   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
-   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 
-   the file LICENSE for additional details.
-
- */
 
 #include "bacula.h"
 #include "stored.h"
 
+#ifdef USE_FAKETAPE
+#include "faketape.h"
+#endif
+
 /* Dummy functions */
 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
 
@@ -66,6 +83,7 @@ static void rewindcmd();
 static void clearcmd();
 static void wrcmd();
 static void rrcmd();
+static void rbcmd();
 static void eodcmd();
 static void fillcmd();
 static void qfillcmd();
@@ -78,7 +96,6 @@ static bool my_mount_next_read_volume(DCR *dcr);
 static void scan_blocks();
 static void set_volume_name(const char *VolName, int volnum);
 static void rawfill_cmd();
-static void bfill_cmd();
 static bool open_the_device();
 static void autochangercmd();
 static void do_unfill();
@@ -96,6 +113,7 @@ static char *argv[MAX_CMD_ARGS];
 static int argc;
 
 static int quickie_count = 0;
+static uint64_t write_count = 0;
 static BSR *bsr = NULL;
 static int signals = TRUE;
 static bool ok;
@@ -151,6 +169,7 @@ int main(int margc, char *margv[])
    setlocale(LC_ALL, "");
    bindtextdomain("bacula", LOCALEDIR);
    textdomain("bacula");
+   init_stack_dump();
 
    /* Sanity checks */
    if (TAPE_BSIZE % B_DEV_BSIZE != 0 || TAPE_BSIZE / B_DEV_BSIZE == 0) {
@@ -160,13 +179,13 @@ int main(int margc, char *margv[])
    if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE)-1))) {
       Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"), TAPE_BSIZE);
    }
-   if (sizeof(off_t) < 8) {
-      Pmsg1(-1, _("\n\n!!!! Warning large disk addressing disabled. off_t=%d should be 8 or more !!!!!\n\n\n"),
-         sizeof(off_t));
+   if (sizeof(boffset_t) < 8) {
+      Pmsg1(-1, _("\n\n!!!! Warning large disk addressing disabled. boffset_t=%d should be 8 or more !!!!!\n\n\n"),
+         sizeof(boffset_t));
    }
    x32 = 123456789;
    bsnprintf(buf, sizeof(buf), "%u", x32);
-   i = bsscanf(buf, "%u", &y32);
+   i = bsscanf(buf, "%lu", &y32);
    if (i != 1 || x32 != y32) {
       Pmsg3(-1, _("32 bit printf/scanf problem. i=%d x32=%u y32=%u\n"), i, x32, y32);
       exit(1);
@@ -177,7 +196,8 @@ int main(int margc, char *margv[])
    bsnprintf(buf, sizeof(buf), "%" llu, x64);
    i = bsscanf(buf, "%llu", &y64);
    if (i != 1 || x64 != y64) {
-      Pmsg3(-1, _("64 bit printf/scanf problem. i=%d x64=%" llu " y64=%" llu "\n"), i, x64, y64);
+      Pmsg3(-1, _("64 bit printf/scanf problem. i=%d x64=%" llu " y64=%" llu "\n"), 
+            i, x64, y64);
       exit(1);
    }
 
@@ -187,6 +207,8 @@ int main(int margc, char *margv[])
    my_name_is(margc, margv, "btape");
    init_msg(NULL, NULL);
 
+   OSDependentInit();
+
    while ((ch = getopt(margc, margv, "b:c:d:psv?")) != -1) {
       switch (ch) {
       case 'b':                    /* bootstrap file */
@@ -202,9 +224,13 @@ int main(int margc, char *margv[])
          break;
 
       case 'd':                    /* set debug level */
-         debug_level = atoi(optarg);
-         if (debug_level <= 0) {
-            debug_level = 1;
+         if (*optarg == 't') {
+            dbg_timestamp = true;
+         } else {
+            debug_level = atoi(optarg);
+            if (debug_level <= 0) {
+               debug_level = 1;
+            }
          }
          break;
 
@@ -265,6 +291,16 @@ int main(int margc, char *margv[])
    if (!dev) {
       exit(1);
    }
+   if (dev->is_dvd()) {
+      Pmsg0(000, _("btape does not work with DVD storage.\n"));
+      usage();
+      exit(1);
+   }
+   if (!dev->is_tape()) {
+      Pmsg0(000, _("btape only works with tape storage.\n"));
+      usage();
+      exit(1);
+   }
    dcr = jcr->dcr;
    if (!open_the_device()) {
       goto terminate;
@@ -306,6 +342,8 @@ static void terminate_btape(int stat)
       dev->term();
    }
 
+   free_volume_list();
+
    if (debug_level > 10)
       print_memory_pool_stats();
 
@@ -325,21 +363,23 @@ static void terminate_btape(int stat)
 static bool open_the_device()
 {
    DEV_BLOCK *block;
+   bool ok = true;
 
    block = new_block(dev);
-   lock_device(dev);
+   dev->r_dlock();
    Dmsg1(200, "Opening device %s\n", dcr->VolumeName);
    if (dev->open(dcr, OPEN_READ_WRITE) < 0) {
       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg);
-      unlock_device(dev);
-      free_block(block);
-      return false;
+      ok = false;
+      goto bail_out;
    }
    Pmsg1(000, _("open device %s: OK\n"), dev->print_name());
    dev->set_append();                 /* put volume in append mode */
-   unlock_device(dev);
+
+bail_out:
+   dev->dunlock();
    free_block(block);
-   return true;
+   return ok;
 }
 
 
@@ -367,7 +407,8 @@ static void labelcmd()
       }
    }
    dev->rewind(dcr);
-   write_new_volume_label_to_dev(dcr, cmd, "Default");
+   dev->weof(1);
+   write_new_volume_label_to_dev(dcr, cmd, "Default", false,/*no relabel*/ true /* label dvd now */);
    Pmsg1(-1, _("Wrote Volume label for volume \"%s\".\n"), cmd);
 }
 
@@ -482,7 +523,7 @@ static void weofcmd()
  */
 static void eomcmd()
 {
-   if (!dev->eod()) {
+   if (!dev->eod(dcr)) {
       Pmsg1(0, "%s", dev->bstrerror());
       return;
    } else {
@@ -695,14 +736,14 @@ static int re_read_block_test()
       Pmsg1(0, _("Wrote third record of %d bytes.\n"), rec->data_len);
    }
    weofcmd();
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    if (!dev->bsf(1)) {
       Pmsg1(0, _("Backspace file failed! ERR=%s\n"), dev->bstrerror());
       goto bail_out;
    }
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       if (!dev->bsf(1)) {
          Pmsg1(0, _("Backspace file failed! ERR=%s\n"), dev->bstrerror());
          goto bail_out;
@@ -716,13 +757,13 @@ static int re_read_block_test()
    Pmsg0(0, _("Backspace record OK.\n"));
    if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) {
       berrno be;
-      Pmsg1(0, _("Read block failed! ERR=%s\n"), be.strerror(dev->dev_errno));
+      Pmsg1(0, _("Read block failed! ERR=%s\n"), be.bstrerror(dev->dev_errno));
       goto bail_out;
    }
    memset(rec->data, 0, rec->data_len);
-   if (!read_record_from_block(block, rec)) {
+   if (!read_record_from_block(dcr, block, rec)) {
       berrno be;
-      Pmsg1(0, _("Read block failed! ERR=%s\n"), be.strerror(dev->dev_errno));
+      Pmsg1(0, _("Read block failed! ERR=%s\n"), be.bstrerror(dev->dev_errno));
       goto bail_out;
    }
    for (int i=0; i<len; i++) {
@@ -809,7 +850,7 @@ static int write_read_test()
    }
    Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
    weofcmd();
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    if (!dev->rewind(dcr)) {
@@ -828,13 +869,13 @@ read_again:
                goto read_again;
             }
          }
-         Pmsg2(0, _("Read block %d failed! ERR=%s\n"), i, be.strerror(dev->dev_errno));
+         Pmsg2(0, _("Read block %d failed! ERR=%s\n"), i, be.bstrerror(dev->dev_errno));
          goto bail_out;
       }
       memset(rec->data, 0, rec->data_len);
-      if (!read_record_from_block(block, rec)) {
+      if (!read_record_from_block(dcr, block, rec)) {
          berrno be;
-         Pmsg2(0, _("Read record failed. Block %d! ERR=%s\n"), i, be.strerror(dev->dev_errno));
+         Pmsg2(0, _("Read record failed. Block %d! ERR=%s\n"), i, be.bstrerror(dev->dev_errno));
          goto bail_out;
       }
       p = (int *)rec->data;
@@ -921,7 +962,7 @@ static int position_test()
    }
    Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
    weofcmd();
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    if (!dev->rewind(dcr)) {
@@ -969,7 +1010,7 @@ static int position_test()
          continue;
       }
       Pmsg2(-1, _("Reposition to file:block %d:%d\n"), file, blk);
-      if (!dev->reposition(file, blk)) {
+      if (!dev->reposition(dcr, file, blk)) {
          Pmsg0(0, _("Reposition error.\n"));
          goto bail_out;
       }
@@ -984,7 +1025,7 @@ read_again:
             }
          }
          Pmsg4(0, _("Read block %d failed! file=%d blk=%d. ERR=%s\n\n"),
-            recno, file, blk, be.strerror(dev->dev_errno));
+            recno, file, blk, be.bstrerror(dev->dev_errno));
          Pmsg0(0, _("This may be because the tape drive block size is not\n"
                     " set to variable blocking as normally used by Bacula.\n"
                     " Please see the Tape Testing chapter in the manual and \n"
@@ -999,9 +1040,9 @@ read_again:
          goto bail_out;
       }
       memset(rec->data, 0, rec->data_len);
-      if (!read_record_from_block(block, rec)) {
+      if (!read_record_from_block(dcr, block, rec)) {
          berrno be;
-         Pmsg1(0, _("Read record failed! ERR=%s\n"), be.strerror(dev->dev_errno));
+         Pmsg1(0, _("Read record failed! ERR=%s\n"), be.bstrerror(dev->dev_errno));
          goto bail_out;
       }
       p = (int *)rec->data;
@@ -1049,7 +1090,7 @@ static int append_test()
    wrcmd();
    wrcmd();
    weofcmd();     /* end file 2 */
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    dev->close();              /* release device */
@@ -1069,7 +1110,7 @@ static int append_test()
    Pmsg0(-1, _("\nNow the important part, I am going to attempt to append to the tape.\n\n"));
    wrcmd();
    weofcmd();
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    rewindcmd();
@@ -1098,7 +1139,7 @@ static int autochanger_test()
    int sleep_time = 0;
 
    Dmsg1(100, "Max changer wait = %d sec\n", timeout);
-   if (!dev_cap(dev, CAP_AUTOCHANGER)) {
+   if (!dev->has_cap(CAP_AUTOCHANGER)) {
       return 1;
    }
    if (!(dcr->device && dcr->device->changer_name && dcr->device->changer_command)) {
@@ -1135,7 +1176,7 @@ try_again:
    } else {
       berrno be;
       Pmsg1(-1, _("3991 Bad autochanger command: %s\n"), changer);
-      Pmsg2(-1, _("3991 result=\"%s\": ERR=%s\n"), results, be.strerror(status));
+      Pmsg2(-1, _("3991 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status));
       goto bail_out;
    }
    if (loaded) {
@@ -1157,7 +1198,7 @@ try_again:
       if (status != 0) {
          berrno be;
          Pmsg1(-1, _("3992 Bad autochanger command: %s\n"), changer);
-         Pmsg2(-1, _("3992 result=\"%s\": ERR=%s\n"), results, be.strerror(status));
+         Pmsg2(-1, _("3992 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status));
       }
    }
 
@@ -1180,7 +1221,7 @@ try_again:
    } else {
       berrno be;
       Pmsg1(-1, _("3993 Bad autochanger command: %s\n"), changer);
-      Pmsg2(-1, _("3993 result=\"%s\": ERR=%s\n"), results, be.strerror(status));
+      Pmsg2(-1, _("3993 result=\"%s\": ERR=%s\n"), results, be.bstrerror(status));
       goto bail_out;
    }
 
@@ -1265,7 +1306,7 @@ static int fsf_test()
    weofcmd();     /* end file 3 */
    wrcmd();
    weofcmd();     /* end file 4 */
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
 
@@ -1328,10 +1369,10 @@ test_again:
 
 bail_out:
    Pmsg0(-1, _("\nThe forward space file test failed.\n"));
-   if (dev_cap(dev, CAP_FASTFSF)) {
+   if (dev->has_cap(CAP_FASTFSF)) {
       Pmsg0(-1, _("You have Fast Forward Space File enabled.\n"
               "I am turning it off then retrying the test.\n"));
-      dev->capabilities &= ~CAP_FASTFSF;
+      dev->clear_cap(CAP_FASTFSF);
       set_off = true;
       goto test_again;
    }
@@ -1366,13 +1407,13 @@ static void testcmd()
       goto all_done;
    }
    if (stat == -1) {                  /* first test failed */
-      if (dev_cap(dev, CAP_EOM) || dev_cap(dev, CAP_FASTFSF)) {
+      if (dev->has_cap(CAP_EOM) || dev->has_cap(CAP_FASTFSF)) {
          Pmsg0(-1, _("\nAppend test failed. Attempting again.\n"
                    "Setting \"Hardware End of Medium = no\n"
                    "    and \"Fast Forward Space File = no\n"
                    "and retrying append test.\n\n"));
-         dev->capabilities &= ~CAP_EOM; /* turn off eom */
-         dev->capabilities &= ~CAP_FASTFSF; /* turn off fast fsf */
+         dev->clear_cap(CAP_EOM);      /* turn off eom */
+         dev->clear_cap(CAP_FASTFSF);  /* turn off fast fsf */
          stat = append_test();
          if (stat == 1) {
             Pmsg0(-1, _("\n\nIt looks like the test worked this time, please add:\n\n"
@@ -1496,6 +1537,14 @@ static void fsrcmd()
    }
 }
 
+/*
+ * Read a Bacula block from the tape
+ */
+static void rbcmd()
+{
+   dev->open(dcr, OPEN_READ_ONLY);
+   read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK);  
+}
 
 /*
  * Write a Bacula block to the tape
@@ -1506,6 +1555,9 @@ static void wrcmd()
    DEV_RECORD *rec = dcr->rec;
    int i;
 
+   if (!dev->is_open()) {
+      open_the_device();
+   }
    sm_check(__FILE__, __LINE__, false);
    empty_block(block);
    if (verbose > 1) {
@@ -1552,13 +1604,13 @@ static void rrcmd()
       len = 1024;
    }
    buf = (char *)malloc(len);
-   stat = read(dev->fd, buf, len);
+   stat = read(dev->fd(), buf, len);
    if (stat > 0 && stat <= len) {
       errno = 0;
    }
    berrno be;
    Pmsg3(0, _("Read of %d bytes gives stat=%d. ERR=%s\n"),
-      len, stat, be.strerror());
+      len, stat, be.bstrerror());
    free(buf);
 }
 
@@ -1583,15 +1635,15 @@ static void scancmd()
       Pmsg0(0, _("End of tape\n"));
       return;
    }
-   update_pos_dev(dev);
+   dev->update_pos(dcr);
    tot_files = dev->file;
    Pmsg1(0, _("Starting scan at file %u\n"), dev->file);
    for (;;) {
-      if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) {
+      if ((stat = read(dev->fd(), buf, sizeof(buf))) < 0) {
          berrno be;
          dev->clrerror(-1);
          Mmsg2(dev->errmsg, _("read error on %s. ERR=%s.\n"),
-            dev->dev_name, be.strerror());
+            dev->dev_name, be.bstrerror());
          Pmsg2(0, _("Bad status from read %d. ERR=%s\n"), stat, dev->bstrerror());
          if (blocks > 0) {
             if (blocks==1) {
@@ -1606,7 +1658,7 @@ static void scancmd()
       Dmsg1(200, "read status = %d\n", stat);
 /*    sleep(1); */
       if (stat != block_size) {
-         update_pos_dev(dev);
+         dev->update_pos(dcr);
          if (blocks > 0) {
             if (blocks==1) {
                printf(_("1 block of %d bytes in file %d\n"), block_size, dev->file);
@@ -1619,7 +1671,7 @@ static void scancmd()
          block_size = stat;
       }
       if (stat == 0) {                /* EOF */
-         update_pos_dev(dev);
+         dev->update_pos(dcr);
          printf(_("End of File mark.\n"));
          /* Two reads of zero means end of tape */
          if (dev->state & ST_EOF)
@@ -1639,7 +1691,7 @@ static void scancmd()
          bytes += stat;
       }
    }
-   update_pos_dev(dev);
+   dev->update_pos(dcr);
    tot_files = dev->file - tot_files;
    printf(_("Total files=%d, blocks=%d, bytes = %s\n"), tot_files, tot_blocks,
       edit_uint64_with_commas(bytes, ec1));
@@ -1665,7 +1717,7 @@ static void scan_blocks()
    bytes = 0;
 
    empty_block(block);
-   update_pos_dev(dev);
+   dev->update_pos(dcr);
    tot_files = dev->file;
    for (;;) {
       if (!read_block_from_device(dcr, NO_BLOCK_NUMBER_CHECK)) {
@@ -1731,7 +1783,7 @@ static void scan_blocks()
          block->VolSessionId, block->VolSessionTime);
       if (verbose == 1) {
          DEV_RECORD *rec = new_record();
-         read_record_from_block(block, rec);
+         read_record_from_block(dcr, block, rec);
          Pmsg8(-1, _("Blk_block: %u dev_blk=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
               block->BlockNumber, dev->block_num, block->block_len,
               FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
@@ -1821,14 +1873,7 @@ static void fillcmd()
    min_block_size = dev->min_block_size;
    dev->min_block_size = dev->max_block_size;
    set_volume_name("TestVolume1", 1);
-
-   if (!dev->rewind(dcr)) {
-      Pmsg0(000, _("Rewind failed.\n"));
-   }
-   if (!dev->weof(1)) {
-      Pmsg0(000, _("Write EOF failed.\n"));
-   }
-   labelcmd();
+   dir_ask_sysop_to_create_appendable_volume(dcr);
    dev->set_append();                 /* force volume to be relabeled */
 
    /*
@@ -1865,7 +1910,7 @@ static void fillcmd()
     * Put some random data in the record
     */
    fd = open("/dev/urandom", O_RDONLY);
-   if (fd) {
+   if (fd != -1) {
       read(fd, rec.data, rec.data_len);
       close(fd);
    } else {
@@ -1881,8 +1926,8 @@ static void fillcmd()
     */
    jcr->dcr->VolFirstIndex = 0;
    time(&jcr->run_time);              /* start counting time for rates */
-   localtime_r(&jcr->run_time, &tm);
-   strftime(buf1, sizeof(buf1), "%T", &tm);
+   (void)localtime_r(&jcr->run_time, &tm);
+   strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm);
    if (simple) {
       Pmsg1(-1, _("%s Begin writing Bacula records to tape ...\n"), buf1);
    } else {
@@ -1935,8 +1980,8 @@ static void fillcmd()
           */
          if ((block->BlockNumber % 32000) == 0) {
             now = time(NULL);
-            localtime_r(&now, &tm);
-            strftime(buf1, sizeof(buf1), "%T", &tm);
+            (void)localtime_r(&now, &tm);
+            strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm);
             Pmsg1(-1, _("%s Flush block, write EOF\n"), buf1);
             flush_block(block, 0);
             dev->weof(1);
@@ -1958,7 +2003,9 @@ static void fillcmd()
 
       /* Get out after writing 10 blocks to the second tape */
       if (BlockNumber > 10 && stop != 0) {      /* get out */
-         Pmsg0(-1, "Done writing ...\n");
+         char ed1[50];
+         Pmsg1(-1, "Done writing %s records ...\n", 
+             edit_uint64_with_commas(write_count, ed1));
          break;
       }
    }
@@ -2008,12 +2055,12 @@ static void fillcmd()
    } else {
       berrno be;
       Pmsg2(-1, _("Could not create state file: %s ERR=%s\n"), buf,
-                 be.strerror());
+                 be.bstrerror());
    }
 
    now = time(NULL);
-   localtime_r(&now, &tm);
-   strftime(buf1, sizeof(buf1), "%T", &tm);
+   (void)localtime_r(&now, &tm);
+   strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm);
    if (simple) {
       Pmsg3(-1, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"),
          buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num);
@@ -2065,7 +2112,7 @@ static void unfillcmd()
    } else {
       berrno be;
       Pmsg2(-1, _("\nCould not find the state file: %s ERR=%s\n"
-             "You must redo the fill command.\n"), buf, be.strerror());
+             "You must redo the fill command.\n"), buf, be.bstrerror());
       return;
    }
    do_unfill();
@@ -2075,15 +2122,15 @@ static void unfillcmd()
 static void do_unfill()
 {
    DEV_BLOCK *block = dcr->block;
-   bool autochanger;
+   int autochanger;
 
    dumped = 0;
    VolBytes = 0;
    LastBlock = 0;
 
    Dmsg0(20, "Enter do_unfill\n");
-   dev->capabilities |= CAP_ANONVOLS; /* allow reading any volume */
-   dev->capabilities &= ~CAP_LABEL;   /* don't label anything here */
+   dev->set_cap(CAP_ANONVOLS);        /* allow reading any volume */
+   dev->clear_cap(CAP_LABEL);         /* don't label anything here */
 
    end_of_tape = 0;
 
@@ -2092,33 +2139,43 @@ static void do_unfill()
    file_index = 0;
    if (last_block) {
       free_block(last_block);
+      last_block = NULL;
    }
    last_block_num = last_block_num1;
    last_file = last_file1;
    last_block = last_block1;
 
+   free_restore_volume_list(jcr);
+   jcr->bsr = NULL;
+   bstrncpy(dcr->VolumeName, "TestVolume1|TestVolume2", sizeof(dcr->VolumeName));
+   create_restore_volume_list(jcr);
+   if (jcr->VolList != NULL) {
+      jcr->VolList->Slot = 1;
+      if (jcr->VolList->next != NULL) {
+         jcr->VolList->next->Slot = 2;
+      }
+   }
+
+   set_volume_name("TestVolume1", 1);
+
    if (!simple) {
       /* Multiple Volume tape */
       /* Close device so user can use autochanger if desired */
-      if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+      if (dev->has_cap(CAP_OFFLINEUNMOUNT)) {
          dev->offline();
       }
       autochanger = autoload_device(dcr, 1, NULL);
-      if (!autochanger) {
+      if (autochanger != 1) {
          dev->close();
          get_cmd(_("Mount first tape. Press enter when ready: "));
       }
-      free_restore_volume_list(jcr);
-      jcr->dcr = new_dcr(jcr, dev);
-      set_volume_name("TestVolume1", 1);
-      jcr->bsr = NULL;
-      create_restore_volume_list(jcr);
-      dev->close();
-      dev->num_writers = 0;
-      if (!acquire_device_for_read(dcr)) {
-         Pmsg1(-1, "%s", dev->errmsg);
-         goto bail_out;
-      }
+   }
+
+   dev->close();
+   dev->num_writers = 0;
+   if (!acquire_device_for_read(dcr)) {
+      Pmsg1(-1, "%s", dev->errmsg);
+      goto bail_out;
    }
    /*
     * We now have the first tape mounted.
@@ -2136,7 +2193,7 @@ static void do_unfill()
    read_records(dcr, quickie_cb, my_mount_next_read_volume);
    Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num,
          last_file, last_block_num);
-   if (!dev->reposition(last_file, last_block_num)) {
+   if (!dev->reposition(dcr, last_file, last_block_num)) {
       Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror());
       goto bail_out;
    }
@@ -2163,16 +2220,14 @@ static void do_unfill()
 
    /* Multiple Volume tape */
    /* Close device so user can use autochanger if desired */
-   if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+   if (dev->has_cap(CAP_OFFLINEUNMOUNT)) {
       dev->offline();
    }
 
-   free_restore_volume_list(jcr);
    set_volume_name("TestVolume2", 2);
-   jcr->bsr = NULL;
-   create_restore_volume_list(jcr);
+
    autochanger = autoload_device(dcr, 1, NULL);
-   if (!autochanger) {
+   if (autochanger != 1) {
       dev->close();
       get_cmd(_("Mount second tape. Press enter when ready: "));
    }
@@ -2187,7 +2242,7 @@ static void do_unfill()
     * on the previous tape.
     */
    Pmsg2(-1, _("Reposition from %u:%u to 0:1\n"), dev->file, dev->block_num);
-   if (!dev->reposition(0, 1)) {
+   if (!dev->reposition(dcr, 0, 1)) {
       Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror());
       goto bail_out;
    }
@@ -2203,7 +2258,7 @@ static void do_unfill()
    /* Now find and compare the last block */
    Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num,
          last_file, last_block_num);
-   if (!dev->reposition(last_file, last_block_num)) {
+   if (!dev->reposition(dcr, last_file, last_block_num)) {
       Pmsg1(-1, _("Reposition error. ERR=%s\n"), dev->bstrerror());
       goto bail_out;
    }
@@ -2283,7 +2338,7 @@ static int flush_block(DEV_BLOCK *block, int dump)
    DEV_BLOCK *tblock;
    uint32_t this_file, this_block_num;
 
-   lock_device(dev);
+   dev->r_dlock();
    if (!this_block) {
       this_block = new_block(dev);
    }
@@ -2338,12 +2393,12 @@ static int flush_block(DEV_BLOCK *block, int dump)
          if (!fixup_device_block_write_error(jcr->dcr)) {
             Pmsg1(000, _("Cannot fixup device error. %s\n"), dev->bstrerror());
             ok = false;
-            unlock_device(dev);
+            dev->dunlock();
             return 0;
          }
          BlockNumber = 0;             /* start counting for second tape */
       }
-      unlock_device(dev);
+      dev->dunlock();
       return 1;                       /* end of tape reached */
    }
 
@@ -2362,7 +2417,7 @@ static int flush_block(DEV_BLOCK *block, int dump)
    last_file = this_file;
    last_block_num = this_block_num;
 
-   unlock_device(dev);
+   dev->dunlock();
    return 1;
 }
 
@@ -2411,7 +2466,7 @@ static void qfillcmd()
    }
    printf("\n");
    weofcmd();
-   if (dev_cap(dev, CAP_TWOEOF)) {
+   if (dev->has_cap(CAP_TWOEOF)) {
       weofcmd();
    }
    rewindcmd();
@@ -2449,7 +2504,11 @@ static void rawfill_cmd()
    Pmsg1(0, _("Begin writing raw blocks of %u bytes.\n"), block->buf_len);
    for ( ;; ) {
       *p = block_num;
-      stat = write(dev->fd, block->buf, block->buf_len);
+      if (dev->is_tape()) {
+         stat = tape_write(dev->fd(), block->buf, block->buf_len);
+      } else {
+         stat = write(dev->fd(), block->buf, block->buf_len);
+      }
       if (stat == (int)block->buf_len) {
          if ((block_num++ % 100) == 0) {
             printf("+");
@@ -2467,89 +2526,42 @@ static void rawfill_cmd()
    printf("\n");
    berrno be;
    printf(_("Write failed at block %u. stat=%d ERR=%s\n"), block_num, stat,
-      be.strerror(my_errno));
+      be.bstrerror(my_errno));
    weofcmd();
 }
 
 
-/*
- * Fill a tape using Bacula block writes
- */
-static void bfill_cmd()
-{
-   DEV_BLOCK *block = dcr->block;
-   uint32_t block_num = 0;
-   uint32_t *p;
-   int my_errno;
-   int fd;
-   uint32_t i;
-
-   fd = open("/dev/urandom", O_RDONLY);
-   if (fd) {
-      read(fd, block->buf, block->buf_len);
-      close(fd);
-   } else {
-      uint32_t *p = (uint32_t *)block->buf;
-      srandom(time(NULL));
-      for (i=0; i<block->buf_len/sizeof(uint32_t); i++) {
-         p[i] = random();
-      }
-   }
-   p = (uint32_t *)block->buf;
-   Pmsg1(0, _("Begin writing Bacula blocks of %u bytes.\n"), block->buf_len);
-   for ( ;; ) {
-      *p = block_num;
-      block->binbuf = block->buf_len;
-      block->bufp = block->buf + block->binbuf;
-      if (!write_block_to_dev(dcr)) {
-         break;
-      }
-      if ((block_num++ % 100) == 0) {
-         printf("+");
-         fflush(stdout);
-      }
-      p[0] += p[13];
-      for (i=1; i<(block->buf_len/sizeof(uint32_t)-1); i++) {
-         p[i] += p[i-1];
-      }
-   }
-   my_errno = errno;
-   printf("\n");
-   printf(_("Write failed at block %u.\n"), block_num);
-   weofcmd();
-}
-
 
 struct cmdstruct { const char *key; void (*func)(); const char *help; };
 static struct cmdstruct commands[] = {
- {N_("autochanger"),autochangercmd, _("test autochanger")},
- {N_("bsf"),       bsfcmd,       _("backspace file")},
- {N_("bsr"),       bsrcmd,       _("backspace record")},
- {N_("bfill"),     bfill_cmd,    _("fill tape using Bacula writes")},
- {N_("cap"),       capcmd,       _("list device capabilities")},
- {N_("clear"),     clearcmd,     _("clear tape errors")},
- {N_("eod"),       eodcmd,       _("go to end of Bacula data for append")},
- {N_("eom"),       eomcmd,       _("go to the physical end of medium")},
- {N_("fill"),      fillcmd,      _("fill tape, write onto second volume")},
- {N_("unfill"),    unfillcmd,    _("read filled tape")},
- {N_("fsf"),       fsfcmd,       _("forward space a file")},
- {N_("fsr"),       fsrcmd,       _("forward space a record")},
- {N_("help"),      helpcmd,      _("print this command")},
- {N_("label"),     labelcmd,     _("write a Bacula label to the tape")},
- {N_("load"),      loadcmd,      _("load a tape")},
- {N_("quit"),      quitcmd,      _("quit btape")},
- {N_("rawfill"),   rawfill_cmd,  _("use write() to fill tape")},
- {N_("readlabel"), readlabelcmd, _("read and print the Bacula tape label")},
- {N_("rectest"),   rectestcmd,   _("test record handling functions")},
- {N_("rewind"),    rewindcmd,    _("rewind the tape")},
- {N_("scan"),      scancmd,      _("read() tape block by block to EOT and report")},
- {N_("scanblocks"),scan_blocks,  _("Bacula read block by block to EOT and report")},
- {N_("status"),    statcmd,      _("print tape status")},
- {N_("test"),      testcmd,      _("General test Bacula tape functions")},
- {N_("weof"),      weofcmd,      _("write an EOF on the tape")},
- {N_("wr"),        wrcmd,        _("write a single Bacula block")},
- {N_("rr"),        rrcmd,        _("read a single record")},
- {N_("qfill"),     qfillcmd,     _("quick fill command")}
+ {NT_("autochanger"),autochangercmd, _("test autochanger")},
+ {NT_("bsf"),       bsfcmd,       _("backspace file")},
+ {NT_("bsr"),       bsrcmd,       _("backspace record")},
+ {NT_("cap"),       capcmd,       _("list device capabilities")},
+ {NT_("clear"),     clearcmd,     _("clear tape errors")},
+ {NT_("eod"),       eodcmd,       _("go to end of Bacula data for append")},
+ {NT_("eom"),       eomcmd,       _("go to the physical end of medium")},
+ {NT_("fill"),      fillcmd,      _("fill tape, write onto second volume")},
+ {NT_("unfill"),    unfillcmd,    _("read filled tape")},
+ {NT_("fsf"),       fsfcmd,       _("forward space a file")},
+ {NT_("fsr"),       fsrcmd,       _("forward space a record")},
+ {NT_("help"),      helpcmd,      _("print this command")},
+ {NT_("label"),     labelcmd,     _("write a Bacula label to the tape")},
+ {NT_("load"),      loadcmd,      _("load a tape")},
+ {NT_("quit"),      quitcmd,      _("quit btape")},
+ {NT_("rawfill"),   rawfill_cmd,  _("use write() to fill tape")},
+ {NT_("readlabel"), readlabelcmd, _("read and print the Bacula tape label")},
+ {NT_("rectest"),   rectestcmd,   _("test record handling functions")},
+ {NT_("rewind"),    rewindcmd,    _("rewind the tape")},
+ {NT_("scan"),      scancmd,      _("read() tape block by block to EOT and report")},
+ {NT_("scanblocks"),scan_blocks,  _("Bacula read block by block to EOT and report")},
+ {NT_("status"),    statcmd,      _("print tape status")},
+ {NT_("test"),      testcmd,      _("General test Bacula tape functions")},
+ {NT_("weof"),      weofcmd,      _("write an EOF on the tape")},
+ {NT_("wr"),        wrcmd,        _("write a single Bacula block")},
+ {NT_("rr"),        rrcmd,        _("read a single record")},
+ {NT_("rb"),        rbcmd,        _("read a single Bacula block")},
+ {NT_("qfill"),     qfillcmd,     _("quick fill command")}
              };
 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
 
@@ -2559,7 +2571,7 @@ do_tape_cmds()
    unsigned int i;
    bool found;
 
-   while (get_cmd("*")) {
+   while (!quit && get_cmd("*")) {
       sm_check(__FILE__, __LINE__, false);
       found = false;
       parse_args(cmd, &args, &argc, argk, argv, MAX_CMD_ARGS);
@@ -2569,10 +2581,9 @@ do_tape_cmds()
             found = true;
             break;
          }
-      if (!found)
-         Pmsg1(0, _("%s is an illegal command\n"), cmd);
-      if (quit)
-         break;
+      if (!found) {
+         Pmsg1(0, _("\"%s\" is an invalid command\n"), cmd);
+      }
    }
 }
 
@@ -2590,17 +2601,18 @@ static void helpcmd()
 static void usage()
 {
    fprintf(stderr, _(
-"Copyright (C) 2000-2005 Kern Sibbald.\n"
+PROG_COPYRIGHT
 "\nVersion: %s (%s)\n\n"
 "Usage: btape <options> <device_name>\n"
 "       -b <file>   specify bootstrap file\n"
 "       -c <file>   set configuration file to file\n"
-"       -d <nn>     set debug level to nn\n"
+"       -d <nn>     set debug level to <nn>\n"
+"       -dt         print timestamp in debug output\n"
 "       -p          proceed inspite of I/O errors\n"
 "       -s          turn off signals\n"
 "       -v          be verbose\n"
 "       -?          print this message.\n"
-"\n"), VERSION, BDATE);
+"\n"), 2000, VERSION, BDATE);
 
 }
 
@@ -2641,7 +2653,7 @@ get_cmd(const char *prompt)
 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
 bool    dir_send_job_status(JCR *jcr) {return 1;}
 
-bool dir_update_volume_info(DCR *dcr, bool relabel)
+bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten)
 {
    return 1;
 }
@@ -2667,14 +2679,13 @@ bool dir_find_next_appendable_volume(DCR *dcr)
    return dcr->VolumeName[0] != 0;
 }
 
-bool dir_ask_sysop_to_mount_volume(DCR *dcr)
+bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /* mode */)
 {
    DEVICE *dev = dcr->dev;
    Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
    if (dcr->VolumeName[0] == 0) {
       return dir_ask_sysop_to_create_appendable_volume(dcr);
    }
-   dev->close();
    Pmsg1(-1, "%s", dev->errmsg);           /* print reason */
    if (dcr->VolumeName[0] == 0 || strcmp(dcr->VolumeName, "TestVolume2") == 0) {
       fprintf(stderr, _("Mount second Volume on device %s and press return when ready: "),
@@ -2683,13 +2694,14 @@ bool dir_ask_sysop_to_mount_volume(DCR *dcr)
       fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
          dcr->VolumeName, dev->print_name());
    }
+   dev->close();
    getchar();
    return true;
 }
 
 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
 {
-   bool autochanger;
+   int autochanger;
    DEVICE *dev = dcr->dev;
    Dmsg0(20, "Enter dir_ask_sysop_to_create_appendable_volume\n");
    if (stop == 0) {
@@ -2698,17 +2710,16 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
       set_volume_name("TestVolume2", 2);
    }
    /* Close device so user can use autochanger if desired */
-   if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+   if (dev->has_cap(CAP_OFFLINEUNMOUNT)) {
       dev->offline();
    }
    autochanger = autoload_device(dcr, 1, NULL);
-   if (!autochanger) {
-      dev->close();
+   if (autochanger != 1) {
       fprintf(stderr, _("Mount blank Volume on device %s and press return when ready: "),
          dev->print_name());
+      dev->close();
       getchar();
    }
-   open_device(dcr);
    labelcmd();
    VolumeName = NULL;
    BlockNumber = 0;
@@ -2722,8 +2733,10 @@ static bool my_mount_next_read_volume(DCR *dcr)
    DEV_BLOCK *block = dcr->block;
 
    Dmsg0(20, "Enter my_mount_next_read_volume\n");
-   Pmsg1(000, _("End of Volume \"%s\"\n"), dcr->VolumeName);
+   Pmsg2(000, _("End of Volume \"%s\" %d records.\n"), dcr->VolumeName,
+      quickie_count);
 
+   volume_unused(dcr);             /* release current volume */
    if (LastBlock != block->BlockNumber) {
       VolBytes += block->block_len;
    }
@@ -2742,10 +2755,8 @@ static bool my_mount_next_read_volume(DCR *dcr)
       return false;
    }
 
-   free_restore_volume_list(jcr);
    set_volume_name("TestVolume2", 2);
-   jcr->bsr = NULL;
-   create_restore_volume_list(jcr);
+
    dev->close();
    if (!acquire_device_for_read(dcr)) {
       Pmsg2(0, _("Cannot open Dev=%s, Vol=%s\n"), dev->print_name(), dcr->VolumeName);
@@ -2763,4 +2774,5 @@ static void set_volume_name(const char *VolName, int volnum)
    bstrncpy(dcr->VolCatInfo.VolCatName, VolName, sizeof(dcr->VolCatInfo.VolCatName));
    bstrncpy(dcr->VolumeName, VolName, sizeof(dcr->VolumeName));
    dcr->VolCatInfo.Slot = volnum;
+   dcr->VolCatInfo.InChanger = true;
 }