]> git.sur5r.net Git - bacula/bacula/commitdiff
ebl Add faketape driver to trunk. Must use --enable-faketape
authorEric Bollengier <eric@eb.homelinux.org>
Sun, 11 May 2008 18:39:07 +0000 (18:39 +0000)
committerEric Bollengier <eric@eb.homelinux.org>
Sun, 11 May 2008 18:39:07 +0000 (18:39 +0000)
     in configure to use it.

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6952 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/src/stored/Makefile.in
bacula/src/stored/faketape.c [new file with mode: 0644]
bacula/src/stored/faketape.h [new file with mode: 0644]

index 9c7a8eafd072caac69d0b7682eec425b93d784f9..d2e7ae8bb159ec778eae84363e937190a85b929f 100644 (file)
@@ -20,7 +20,7 @@ first_rule: all
 dummy:
 
 # bacula-sd
-SDOBJS =  stored.o ansi_label.o \
+SDOBJS =  stored.o ansi_label.o faketape.o \
          autochanger.o acquire.o append.o \
          askdir.o authenticate.o \
          block.o butil.o dev.o \
@@ -31,33 +31,33 @@ SDOBJS =  stored.o ansi_label.o \
          spool.o status.o stored_conf.o wait.o
 
 # btape
-TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o \
+TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o faketape.o \
           lock.o ansi_label.o dvd.o ebcdic.o \
           autochanger.o acquire.o mount.o record.o read_record.o \
           reserve.o \
           stored_conf.o match_bsr.o parse_bsr.o scan.o spool.o wait.o
 
 # bls
-BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o \
+BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o faketape.o \
          ansi_label.o dvd.o ebcdic.o lock.o \
          autochanger.o acquire.o mount.o parse_bsr.o record.o  \
          read_record.o reserve.o scan.o stored_conf.o spool.o wait.o
 
 # bextract
-BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \
+BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o faketape.o \
           ansi_label.o dvd.o ebcdic.o lock.o \
           autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \
           pythonsd.o read_record.o reserve.o \
           scan.o stored_conf.o spool.o wait.o
 
 # bscan
-SCNOBJS = bscan.o block.o device.o dev.o label.o \
+SCNOBJS = bscan.o block.o device.o dev.o label.o faketape.o \
          ansi_label.o dvd.o ebcdic.o lock.o \
          autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
          butil.o read_record.o scan.o reserve.o stored_conf.o spool.o wait.o
 
 # bcopy
-COPYOBJS = bcopy.o block.o device.o dev.o label.o \
+COPYOBJS = bcopy.o block.o device.o dev.o label.o faketape.o \
           ansi_label.o dvd.o ebcdic.o lock.o \
           autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
           butil.o read_record.o reserve.o \
diff --git a/bacula/src/stored/faketape.c b/bacula/src/stored/faketape.c
new file mode 100644 (file)
index 0000000..e8792e4
--- /dev/null
@@ -0,0 +1,877 @@
+/*
+   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, which is 
+   listed 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.
+*/
+
+/*
+
+Device {
+  Name = Drive-1                      #
+  Maximum File Size = 800M
+  Maximum Volume Size = 3G
+  Device Type = TAPE
+  Archive Device = /tmp/fake
+  Media Type = DLT-8000
+  AutomaticMount = yes;               # when device opened, read it
+  AlwaysOpen = yes;
+  RemovableMedia = yes;
+  RandomAccess = no;
+}
+
+  Block description :
+
+  block {
+    int32  size;
+    void   *data;
+  }
+
+  EOF description :
+
+  EOF {
+    int32  size=0;
+  }
+
+
+ */
+
+#include "bacula.h"            /* define 64bit file usage */
+#include "stored.h"
+
+#ifdef USE_FAKETAPE
+#include "faketape.h"
+
+static int dbglevel = 10;
+#define FILE_OFFSET 30
+faketape *ftape_list[FTAPE_MAX_DRIVE];
+
+static faketape *get_tape(int fd)
+{
+   ASSERT(fd >= 0);
+
+   if (fd >= FTAPE_MAX_DRIVE) {
+      /* error */
+      return NULL;
+   }
+
+   return ftape_list[fd];
+}
+
+static bool put_tape(faketape *ftape)
+{
+   ASSERT(ftape != NULL);
+
+   int fd = ftape->get_fd();
+   if (fd >= FTAPE_MAX_DRIVE) {
+      /* error */
+      return false;
+   }
+   ftape_list[fd] = ftape;
+   return true;
+}
+
+void faketape_debug(int level)
+{
+   dbglevel = level;
+}
+
+/****************************************************************/
+/* theses function will replace open/read/write/close/ioctl
+ * in bacula core
+ */
+int faketape_open(const char *pathname, int flags)
+{
+   ASSERT(pathname != NULL);
+
+   int fd;
+   faketape *tape = new faketape();
+   fd = tape->open(pathname, flags);
+   if (fd > 0) {
+      put_tape(tape);
+   }
+   return fd;
+}
+
+int faketape_read(int fd, void *buffer, unsigned int count)
+{
+   faketape *tape = get_tape(fd);
+   ASSERT(tape != NULL);
+   return tape->read(buffer, count);
+}
+
+int faketape_write(int fd, const void *buffer, unsigned int count)
+{
+   faketape *tape = get_tape(fd);
+   ASSERT(tape != NULL);
+   return tape->write(buffer, count);
+}
+
+int faketape_close(int fd)
+{
+   faketape *tape = get_tape(fd);
+   ASSERT(tape != NULL);
+   tape->close();
+   delete tape;
+   return 0;
+}
+
+int faketape_ioctl(int fd, unsigned long int request, ...)
+{
+   va_list argp;
+   int result=0;
+
+   faketape *t = get_tape(fd);
+   if (!t) {
+      errno = EBADF;
+      return -1;
+   }
+
+   va_start(argp, request);
+
+   if (request == MTIOCTOP) {
+      result = t->tape_op(va_arg(argp, mtop *));
+   } else if (request == MTIOCGET) {
+      result = t->tape_get(va_arg(argp, mtget *));
+   } else if (request == MTIOCPOS) {
+      result = t->tape_pos(va_arg(argp, mtpos *));
+   } else {
+      errno = ENOTTY;
+      result = -1;
+   }
+   va_end(argp);
+
+   return result;
+}
+
+/****************************************************************/
+
+int faketape::tape_op(struct mtop *mt_com)
+{
+   int result=0;
+
+   if (!online) {
+      errno = ENOMEDIUM;
+      return -1;
+   }
+   
+   switch (mt_com->mt_op)
+   {
+   case MTRESET:
+   case MTNOP:
+   case MTSETDRVBUFFER:
+      break;
+
+   default:
+   case MTRAS1:
+   case MTRAS2:
+   case MTRAS3:
+   case MTSETDENSITY:
+      errno = ENOTTY;
+      result = -1;
+      break;
+
+   case MTFSF:                 /* Forward space over mt_count filemarks. */
+      result = fsf(mt_com->mt_count);
+      break;
+
+   case MTBSF:                 /* Backward space over mt_count filemarks. */
+      result = bsf(mt_com->mt_count);
+      break;
+
+   case MTFSR:     /* Forward space over mt_count records (tape blocks). */
+/*
+    file number = 1
+    block number = 0
+   
+    file number = 1
+    block number = 1
+   
+    mt: /dev/lto2: Erreur d'entree/sortie
+   
+    file number = 2
+    block number = 0
+*/
+      /* tester si on se trouve a la fin du fichier */
+      result = fsr(mt_com->mt_count);
+      break;
+
+   case MTBSR:      /* Backward space over mt_count records (tape blocks). */
+      result = bsr(mt_com->mt_count);
+      break;
+
+   case MTWEOF:                        /* Write mt_count filemarks. */
+      result = weof(mt_com->mt_count);
+      break;
+
+   case MTREW:                 /* Rewind. */
+      Dmsg0(dbglevel, "rewind faketape\n");
+      atEOF = atEOD = false;
+      atBOT = true;
+      current_file = 0;
+      current_block = 0;
+      seek_file();
+      break;
+
+   case MTOFFL:                        /* put tape offline */
+      result = offline();
+      break;
+
+   case MTRETEN:               /* Re-tension tape. */
+      result = 0;
+      break;
+
+   case MTBSFM:                        /* not used by bacula */
+      errno = EIO;
+      result = -1;
+      break;
+
+   case MTFSFM:                        /* not used by bacula */
+      errno = EIO;
+      result = -1;
+      break;
+
+   case MTEOM:/* Go to the end of the recorded media (for appending files). */
+/*
+   file number = 3
+   block number = -1
+*/
+      /* Can be at EOM */
+      atBOT = false;
+      atEOF = false;
+      atEOD = true;
+      atEOT = false;
+
+      current_file = last_file;
+      current_block = -1;
+      seek_file();
+      break;
+
+   case MTERASE:               /* not used by bacula */
+      atEOD = true;
+      atEOF = false;
+      atEOT = false;
+
+      current_file = 0;
+      current_block = -1;
+      seek_file();
+      truncate_file();
+      break;
+
+   case MTSETBLK:
+      break;
+
+   case MTSEEK:
+      break;
+
+   case MTTELL:
+      break;
+
+   case MTFSS:
+      break;
+
+   case MTBSS:
+      break;
+
+   case MTWSM:
+      break;
+
+   case MTLOCK:
+      break;
+
+   case MTUNLOCK:
+      break;
+
+   case MTLOAD:
+      break;
+
+   case MTUNLOAD:
+      break;
+
+   case MTCOMPRESSION:
+      break;
+
+   case MTSETPART:
+      break;
+
+   case MTMKPART:
+      break;
+   }
+
+   return result == 0 ? 0 : -1;
+}
+
+int faketape::tape_get(struct mtget *mt_get)
+{
+   int density = 1;
+   int block_size = 1024;
+
+   mt_get->mt_type = MT_ISSCSI2;
+   mt_get->mt_blkno = current_block;
+   mt_get->mt_fileno = current_file;
+
+   mt_get->mt_resid = -1;
+//   pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
+
+   /* TODO */
+   mt_get->mt_dsreg = 
+      ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
+      ((block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
+
+
+   mt_get->mt_gstat = 0x00010000;  /* Immediate report mode.*/
+
+   if (atEOF) {
+      mt_get->mt_gstat |= 0x80000000;     // GMT_EOF
+   }
+
+   if (atBOT) {
+      mt_get->mt_gstat |= 0x40000000;     // GMT_BOT
+   }
+   if (atEOT) {
+      mt_get->mt_gstat |= 0x20000000;     // GMT_EOT
+   }
+
+   if (atEOD) {
+      mt_get->mt_gstat |= 0x08000000;     // GMT_EOD
+   }
+
+   if (0) { //WriteProtected) {
+      mt_get->mt_gstat |= 0x04000000;     // GMT_WR_PROT
+   }
+
+   if (online) {
+      mt_get->mt_gstat |= 0x01000000;     // GMT_ONLINE
+   } else {
+      mt_get->mt_gstat |= 0x00040000;  // GMT_DR_OPEN
+   }
+   mt_get->mt_erreg = 0;
+
+   return 0;
+}
+
+int faketape::tape_pos(struct mtpos *mt_pos)
+{
+   if (current_block >= 0) {
+      mt_pos->mt_blkno = current_block;
+      return 0;
+   }
+
+   return -1;
+}
+
+/*
+ * This function try to emulate the append only behavior
+ * of a tape. When you wrote something, data after the
+ * current position are discarded.
+ */
+int faketape::truncate_file()
+{  
+   Dmsg2(dbglevel, "truncate %i:%i\n", current_file, current_block);
+   ftruncate(fd, lseek(fd, 0, SEEK_CUR));
+   last_file = current_file;
+   atEOD=true;
+   return 0;
+}
+
+faketape::faketape()
+{
+   fd = -1;
+
+   atEOF = false;
+   atBOT = false;
+   atEOT = false;
+   atEOD = false;
+   online = false;
+   inplace = false;
+   needEOF = false;
+
+   file_size = 0;
+   last_file = 0;
+   current_file = 0;
+   current_block = -1;
+   current_pos = 0;
+
+   max_block = 1024*1024*1024*1024*8;
+
+}
+
+faketape::~faketape()
+{
+}
+
+int faketape::get_fd()
+{
+   return this->fd;
+}
+
+/*
+ * TODO: check if after a write op, and other tape op put a EOF
+ */
+int faketape::write(const void *buffer, unsigned int count)
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   ASSERT(count > 0);
+   ASSERT(buffer);
+
+   unsigned int nb;
+   Dmsg3(dbglevel, "write len=%i %i:%i\n", count, current_file,current_block);
+
+   if (atEOT) {
+      Dmsg0(dbglevel, "write nothing, EOT !\n");
+      errno = ENOSPC;
+      return -1;
+   }
+
+   if (!inplace) {
+      seek_file();
+   }
+
+   if (!atEOD) {               /* if not at the end of the data */
+      truncate_file();
+   }
+   if (current_block != -1) {
+      current_block++;
+   }
+
+   atBOT = false;
+   atEOF = false;
+   atEOD = true;               /* End of data */
+
+   needEOF = true;             /* next operation need EOF mark */
+
+//   if ((count + file_size) > max_size) {
+//      Dmsg2(dbglevel, 
+//         "EOT writing only %i of %i requested\n", 
+//         max_size - file_size, count);
+//      count = max_size - file_size;
+//      atEOT = true;
+//   }
+
+   uint32_t size = count;
+   ::write(fd, &size, sizeof(uint32_t));
+   nb = ::write(fd, buffer, count);
+   
+   file_size += sizeof(uint32_t) + nb;
+
+   if (nb != count) {
+      atEOT = true;
+      Dmsg2(dbglevel, 
+           "Not enough space writing only %i of %i requested\n", 
+           nb, count);
+   }
+
+   return nb;
+}
+
+int faketape::weof(int count)
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   Dmsg3(dbglevel, "Writing EOF %i:%i last=%i\n", 
+        current_file, current_block,last_file);
+   if (atEOT) {
+      errno = ENOSPC;
+      current_block = -1;
+      return -1;
+   }
+   needEOF = false;
+   truncate_file();            /* nothing after this point */
+
+   /* TODO: check this */
+   current_file += count;
+   current_block = 0;
+
+   uint32_t c=0;
+   seek_file();
+   ::write(fd, &c, sizeof(uint32_t));
+   seek_file();
+
+   atEOD = false;
+   atBOT = false;
+   atEOF = true;
+
+   return 0;
+}
+
+int faketape::fsf(int count)
+{   
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   ASSERT(fd >= 0);
+/*
+ * 1 0 -> fsf -> 2 0 -> fsf -> 2 -1
+ */
+   check_eof();
+
+   int ret;
+   if (atEOT) {
+      current_block = -1;
+      return -1;
+   }
+
+   atBOT = atEOF = false;
+   Dmsg3(dbglevel+1, "fsf %i+%i <= %i\n", current_file, count, last_file);
+
+   if ((current_file + count) <= last_file) {
+      current_file += count;
+      current_block = 0;
+      ret = 0;
+   } else {
+      Dmsg0(dbglevel, "Try to FSF after EOT\n");
+      current_file = last_file ;
+      current_block = -1;
+      atEOD=true;
+      ret = -1;
+   }
+   seek_file();
+   return ret;
+}
+
+int faketape::fsr(int count)
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   ASSERT(fd >= 0);
+   
+   int i,nb, ret=0;
+   off_t where=0;
+   uint32_t s;
+   Dmsg3(dbglevel, "fsr %i:%i count=%i\n", current_file,current_block, count);
+
+   check_eof();
+
+   if (atEOT) {
+      errno = EIO;
+      current_block = -1;
+      return -1;
+   }
+
+   if (atEOD) {
+      errno = EIO;
+      return -1;
+   }
+
+   atBOT = atEOF = false;   
+
+   /* check all block record */
+   for(i=0; (i < count) && !atEOF ; i++) {
+      nb = ::read(fd, &s, sizeof(uint32_t)); /* get size of next block */
+      if (nb == sizeof(uint32_t) && s) {
+        current_block++;
+        where = lseek(fd, s, SEEK_CUR);     /* seek after this block */
+      } else {
+        Dmsg4(dbglevel, "read EOF %i:%i nb=%i s=%i\n",
+              current_file, current_block, nb,s);
+        errno = EIO;
+        ret = -1;
+        if (current_file < last_file) {
+           current_block = 0;
+           current_file++;
+           seek_file();
+        }
+        atEOF = true;          /* stop the loop */
+      }
+   }
+
+   find_maxfile();             /* refresh stats */
+
+   if (where == file_size) {
+      atEOD = true;
+   }
+   return ret;
+}
+
+int faketape::bsr(int count)
+{
+   Dmsg2(dbglevel, "bsr current_block=%i count=%i\n", 
+        current_block, count);
+
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   ASSERT(count == 1);
+   ASSERT(fd >= 0);
+
+   check_eof();
+
+   if (!count) {
+      return 0;
+   }
+
+   int ret=0;
+   int last_f=0;
+   int last_b=0;
+
+   off_t last=-1, last2=-1;
+   off_t orig = lseek(fd, 0, SEEK_CUR);
+   int orig_f = current_file;
+   int orig_b = current_block;
+
+   current_block=0;
+   seek_file();
+
+   do {
+      if (!atEOF) {
+        last2 = last;
+        last = lseek(fd, 0, SEEK_CUR);
+        last_f = current_file;
+        last_b = current_block;
+        Dmsg5(dbglevel, "EOF=%i last=%lli orig=%lli %i:%i\n", 
+              atEOF, last, orig, current_file, current_block);
+      }
+      ret = fsr(1);
+   } while ((lseek(fd, 0, SEEK_CUR) < orig) && (ret == 0));
+
+   if (last2 > 0 && atEOF) {   /* we take the previous position */
+      lseek(fd, last2, SEEK_SET);
+      current_file = last_f;
+      current_block = last_b - 1;
+      Dmsg3(dbglevel, "set offset2=%lli %i:%i\n", 
+           last, current_file, current_block);
+
+   } else if (last > 0) {
+      lseek(fd, last, SEEK_SET);
+      current_file = last_f;
+      current_block = last_b;
+      Dmsg3(dbglevel, "set offset=%lli %i:%i\n", 
+           last, current_file, current_block);
+   } else {
+      lseek(fd, orig, SEEK_SET);
+      current_file = orig_f;
+      current_block = orig_b;
+      return -1;
+   }
+
+   Dmsg2(dbglevel, "bsr %i:%i\n", current_file, current_block);
+   atEOT = atEOF = atEOD = false;
+
+   return 0;
+}
+
+int faketape::bsf(int count)
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   Dmsg3(dbglevel, "bsf %i:%i count=%i\n", current_file, current_block, count);
+   int ret = 0;
+
+   check_eof();
+   atBOT = atEOF = atEOT = atEOD = false;
+
+   if (current_file - count < 0) {
+      current_file = 0;
+      current_block = 0;
+      atBOT = true;
+      errno = EIO;
+      ret = -1;
+   } else {
+      current_file = current_file - count + 1;
+      current_block = -1;
+      seek_file();
+      current_file--;
+      /* go just before last EOF */
+      lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
+   }
+   return ret;
+}
+
+/* 
+ * Put faketape in offline mode
+ */
+int faketape::offline()
+{
+   close();
+   
+   atEOF = false;              /* End of file */
+   atEOT = false;              /* End of tape */
+   atEOD = false;              /* End of data */
+   atBOT = false;              /* Begin of tape */
+   online = false;
+
+   current_file = -1;
+   current_block = -1;
+   last_file = -1;
+   return 0;
+}
+
+int faketape::close()
+{
+   check_eof();
+   ::close(fd);
+   fd = -1;
+   return 0;
+}
+/*
+ **rb
+ **status
+ * EOF Bacula status: file=2 block=0
+ * Device status: EOF ONLINE IM_REP_EN file=2 block=0
+ **rb
+ **status
+ * EOD EOF Bacula status: file=2 block=0
+ * Device status: EOD ONLINE IM_REP_EN file=2 block=-1
+ *
+ */
+
+int faketape::read(void *buffer, unsigned int count)
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   unsigned int nb;
+   uint32_t s;
+   
+   Dmsg2(dbglevel, "read %i:%i\n", current_file, current_block);
+
+   if (atEOT || atEOD) {
+      return 0;
+   }
+
+   if (atEOF) {
+      current_file++;
+      current_block=0;
+      inplace = false;
+      atEOF = false;
+   }
+
+   if (!inplace) {
+      seek_file();
+   }
+
+   atEOD = atBOT = false;
+   current_block++;
+
+   nb = ::read(fd, &s, sizeof(uint32_t));
+   if (nb <= 0) {
+      atEOF = true;
+      return 0;
+   }
+   if (s > count) {            /* not enough buffer to read block */
+      Dmsg2(dbglevel, "Need more buffer to read next block %i > %i\n",s,count);
+      lseek(fd, s, SEEK_CUR);
+      errno = ENOMEM;
+      return -1;
+   }
+   if (!s) {                   /* EOF */
+      atEOF = true;
+      lseek(fd, lseek(fd, 0, SEEK_CUR) - sizeof(uint32_t), SEEK_SET);
+      return 0;
+   }
+   nb = ::read(fd, buffer, s);
+   if (s != nb) {
+      atEOF = true;
+      if (current_file == last_file) {
+        atEOD = true;
+        current_block = -1;
+      }
+      Dmsg0(dbglevel, "EOF during reading\n");
+   }
+   return nb;
+}
+
+int faketape::open(const char *pathname, int uflags)
+{
+   Dmsg2(dbglevel, "faketape::open(%s, %i)\n", pathname, uflags);
+
+   online = true;              /* assume that drive contains a tape */
+
+   struct stat statp;   
+   if (stat(pathname, &statp) != 0) {
+      Dmsg1(dbglevel, "Can't stat on %s\n", pathname);
+      if (uflags & O_NONBLOCK) {
+        online = false;
+        fd = ::open("/dev/null", O_CREAT | O_RDWR | O_LARGEFILE, 0600);
+      }
+   } else {
+      fd = ::open(pathname, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
+   }
+
+   if (fd < 0) {
+      errno = ENOMEDIUM;
+      return -1;
+   }
+
+   /* open volume descriptor and get this->fd */
+   find_maxfile();
+
+   current_block = 0;
+   current_file = 0;
+   needEOF = false;
+   inplace = true;
+   atBOT = true;
+   atEOT = atEOD = false;
+
+   return fd;
+}
+
+/*
+ * read volume to get the last file number
+ */
+int faketape::find_maxfile()
+{
+   struct stat statp;
+   if (fstat(fd, &statp) != 0) {
+      return 0;
+   }
+   last_file = statp.st_size>>FILE_OFFSET;
+   file_size = statp.st_size;
+      
+   current_pos = lseek(fd, 0, SEEK_CUR); /* get current position */
+   Dmsg3(dbglevel+1, "last_file=%i file_size=%u current_pos=%i\n", 
+        last_file, file_size, current_pos);
+
+   return last_file;
+}
+
+int faketape::seek_file()
+{
+   ASSERT(online);
+   ASSERT(current_file >= 0);
+   Dmsg2(dbglevel, "seek_file %i:%i\n", current_file, current_block);
+
+   off_t pos = ((off_t)current_file)<<FILE_OFFSET;
+   if(lseek(fd, pos, SEEK_SET) == -1) {
+      return -1;
+   }
+   last_file = (last_file > current_file)?last_file:current_file;
+   if (current_block > 0) {
+      fsr(current_block);
+   }
+   inplace = true;
+
+   return 0;
+}
+
+void faketape::dump()
+{
+   Dmsg0(dbglevel+1, "===================\n");
+   Dmsg2(dbglevel, "file:block = %i:%i\n", current_file, current_block);
+   Dmsg1(dbglevel+1, "last_file=%i\n", last_file);
+   Dmsg1(dbglevel+1, "file_size=%i\n", file_size);  
+   Dmsg4(dbglevel+1, "EOF=%i EOT=%i EOD=%i BOT=%i\n", 
+        atEOF, atEOT, atEOD, atBOT);  
+}
+
+#endif /* USE_FAKETAPE */
diff --git a/bacula/src/stored/faketape.h b/bacula/src/stored/faketape.h
new file mode 100644 (file)
index 0000000..040f5a8
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+   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, which is
+   listed 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.
+*/
+/*
+ * faketape.h - Emulate the Linux st (scsi tape) driver on file.
+ * for regression and bug hunting purpose
+ *
+ */
+
+#ifndef FAKETAPE_H
+#define FAKETAPE_H
+
+#include <stdarg.h>
+#include <stddef.h>
+#include "bacula.h"
+
+#ifdef USE_FAKETAPE
+
+#define FTAPE_MAX_DRIVE 50
+
+/* 
+ * Theses functions will replace open/read/write
+ */
+int faketape_open(const char *pathname, int flags);
+int faketape_read(int fd, void *buffer, unsigned int count);
+int faketape_write(int fd, const void *buffer, unsigned int count);
+int faketape_close(int fd);
+int faketape_ioctl(int fd, unsigned long int request, ...);
+void faketape_debug(int level);
+
+class faketape {
+private:
+   int         fd;              /* Our file descriptor */
+
+   off_t       file_size;       /* size */
+   off_t       max_block;
+
+   bool        atEOF;           /* End of file */
+   bool        atEOT;           /* End of media */
+   bool        atEOD;           /* End of data */
+   bool        atBOT;           /* Begin of tape */
+   bool        online;          /* volume online */
+   bool        inplace;         /* have to seek before writing ? */
+   bool        needEOF;         /* check if last operation need eof */
+
+   int32_t     last_file;       /* last file of the volume */
+   int32_t     current_file;    /* max 65000 files */
+   int32_t     current_block;   /* max 4G blocks of 1KB */
+   off_t       current_pos;     /* current position in stream */
+
+   void destroy();
+   int find_maxfile();
+   int offline();
+   int truncate_file();
+   int seek_file();
+   void check_eof() { if(needEOF) weof(1);};
+
+public:
+   int fsf(int count);
+   int fsr(int count);
+   int weof(int count);
+   int bsf(int count);
+   int bsr(int count);
+
+   faketape();
+   ~faketape();
+
+   int get_fd();
+   void dump();
+   int open(const char *pathname, int flags);
+   int read(void *buffer, unsigned int count);
+   int write(const void *buffer, unsigned int count);
+   int close();
+
+   int tape_op(struct mtop *mt_com);
+   int tape_get(struct mtget *mt_com);
+   int tape_pos(struct mtpos *mt_com);
+};
+
+#endif /* USE_FAKETAPE */
+#endif /* !FAKETAPE_H */