]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/dev.c
update version
[bacula/bacula] / bacula / src / stored / dev.c
index 50b4dd4a63116755ffd2312d6d2cba1b261883fd..5ce5712eff40713d4a985f8252e2262ed28f7322 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 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.
@@ -20,7 +20,7 @@
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Bacula® is a registered trademark of John Walker.
+   Bacula® is a registered trademark of Kern Sibbald.
    The licensor of Bacula is the Free Software Foundation Europe
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
  *     daemon. More complicated coding (double buffering, writer
  *     thread, ...) is left for a later version.
  *
- *   Version $Id$
  */
 
 /*
  * Handling I/O errors and end of tape conditions are a bit tricky.
- * This is how it is currently done when writting.
+ * This is how it is currently done when writing.
  * On either an I/O error or end of tape,
  * we will stop writing on the physical device (no I/O recovery is
  * attempted at least in this daemon). The state flag will be sent
@@ -89,7 +88,7 @@
 /* Forward referenced functions */
 void set_os_device_parameters(DCR *dcr);   
 static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat);
-static char *mode_to_str(int mode);
+static const char *mode_to_str(int mode);
 
 /*
  * Allocate and initialize the DEVICE structure
@@ -127,6 +126,13 @@ init_dev(JCR *jcr, DEVRES *device)
          device->dev_type = B_TAPE_DEV;
       } else if (S_ISFIFO(statp.st_mode)) {
          device->dev_type = B_FIFO_DEV;
+#ifdef USE_VTAPE
+      /* must set DeviceType = Vtape 
+       * in normal mode, autodetection is disabled
+       */
+      } else if (S_ISREG(statp.st_mode)) { 
+         device->dev_type = B_VTAPE_DEV;
+#endif
       } else if (!(device->cap_bits & CAP_REQMOUNT)) {
          Jmsg2(jcr, M_ERROR, 0, _("%s is an unknown device type. Must be tape or directory\n"
                " or have RequiresMount=yes for DVD. st_mode=%x\n"),
@@ -139,7 +145,7 @@ init_dev(JCR *jcr, DEVRES *device)
 
    dev = (DEVICE *)malloc(sizeof(DEVICE));
    memset(dev, 0, sizeof(DEVICE));
-   dev->Slot = -1;       /* unknown */
+   dev->clear_slot();         /* unknown */
 
    /* Copy user supplied device parameters from Resource */
    dev->dev_name = get_memory(strlen(device->device_name)+1);
@@ -153,6 +159,7 @@ init_dev(JCR *jcr, DEVRES *device)
    dev->max_block_size = device->max_block_size;
    dev->max_volume_size = device->max_volume_size;
    dev->max_file_size = device->max_file_size;
+   dev->max_concurrent_jobs = device->max_concurrent_jobs;
    dev->volume_capacity = device->volume_capacity;
    dev->max_rewind_wait = device->max_rewind_wait;
    dev->max_open_wait = device->max_open_wait;
@@ -162,6 +169,7 @@ init_dev(JCR *jcr, DEVRES *device)
    dev->drive_index = device->drive_index;
    dev->autoselect = device->autoselect;
    dev->dev_type = device->dev_type;
+   dev->init_backend();
    if (dev->is_tape()) { /* No parts on tapes */
       dev->max_part_size = 0;
    } else {
@@ -190,11 +198,12 @@ init_dev(JCR *jcr, DEVRES *device)
          Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"), 
             device->mount_point, be.bstrerror());
       }
-   }
-   if (dev->is_dvd()) {
+   
       if (!device->mount_command || !device->unmount_command) {
          Jmsg0(jcr, M_ERROR_TERM, 0, _("Mount and unmount commands must defined for a device which requires mount.\n"));
       }
+   }
+   if (dev->is_dvd()) {
       if (!device->write_part_command) {
          Jmsg0(jcr, M_ERROR_TERM, 0, _("Write part command must be defined for a device which requires mount.\n"));
       }
@@ -219,6 +228,10 @@ init_dev(JCR *jcr, DEVRES *device)
       Jmsg2(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size.\n"),
          dev->max_block_size, dev->print_name());
    }
+   if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) {
+      Jmsg(jcr, M_ERROR_TERM, 0, _("Max Vol Size < 8 * Max Block Size on device %s\n"), 
+           dev->print_name());
+   }
 
    dev->errmsg = get_pool_memory(PM_EMSG);
    *dev->errmsg = 0;
@@ -247,6 +260,16 @@ init_dev(JCR *jcr, DEVRES *device)
       Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
       Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
    }
+   if ((errstat = pthread_mutex_init(&dev->acquire_mutex, NULL)) != 0) {
+      berrno be;
+      dev->dev_errno = errstat;
+      Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
+      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+   }
+   /* Ensure that we respect this order in P/V operations */
+   bthread_mutex_set_priority(&dev->m_mutex,       PRIO_SD_DEV_ACCESS);
+   bthread_mutex_set_priority(&dev->spool_mutex,   PRIO_SD_DEV_SPOOL);
+   bthread_mutex_set_priority(&dev->acquire_mutex, PRIO_SD_DEV_ACQUIRE);
 #ifdef xxx
    if ((errstat = rwl_init(&dev->lock)) != 0) {
       berrno be;
@@ -264,6 +287,44 @@ init_dev(JCR *jcr, DEVRES *device)
    return dev;
 }
 
+/* Choose the right backend */
+void DEVICE::init_backend()
+{
+
+#ifdef HAVE_WIN32
+   if (is_tape()) {
+      d_open  = win32_tape_open;
+      d_write = win32_tape_write;
+      d_close = win32_tape_close;
+      d_ioctl = win32_tape_ioctl;
+      d_read  = win32_tape_read;
+
+   } else {
+      d_open  = ::open;
+      d_close = ::close;
+      d_ioctl = win32_ioctl;    /* dummy function */
+      d_write = win32_write;    /* win32 read/write are not POSIX */
+      d_read  = win32_read;
+   }
+
+#else  /* POSIX / UNIX Interface */
+   if (is_vtape()) {            /* test backend */
+      d_open  = vtape_open;     /* vtape isn't available for WIN32 or FreeBSD */
+      d_write = vtape_write;
+      d_close = vtape_close;
+      d_ioctl = vtape_ioctl;
+      d_read  = vtape_read;
+
+   } else {                     /* tape and file are using normal io */
+      d_open  = ::open;
+      d_write = ::write;
+      d_close = ::close;
+      d_ioctl = ::ioctl;
+      d_read  = ::read;
+   }
+#endif
+}
+
 /*
  * Open the device with the operating system and
  * initialize buffer pointers.
@@ -285,24 +346,20 @@ DEVICE::open(DCR *dcr, int omode)
       if (openmode == omode) {
          return m_fd;
       } else {
-         if (is_tape()) {
-            tape_close(m_fd);
-         } else {
-            ::close(m_fd);
-         }
+         d_close(m_fd);
          clear_opened();
          Dmsg0(100, "Close fd for mode change.\n");
          preserve = state & (ST_LABEL|ST_APPEND|ST_READ);
       }
    }
    if (dcr) {
-      bstrncpy(VolCatInfo.VolCatName, dcr->VolumeName, sizeof(VolCatInfo.VolCatName));
+      dcr->setVolCatName(dcr->VolumeName);
+      VolCatInfo = dcr->VolCatInfo;    /* structure assign */
    }
 
    Dmsg4(100, "open dev: type=%d dev_name=%s vol=%s mode=%s\n", dev_type,
-         print_name(), VolCatInfo.VolCatName, mode_to_str(omode));
+         print_name(), getVolCatName(), mode_to_str(omode));
    state &= ~(ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF);
-   Slot = -1;          /* unknown slot */
    label_type = B_BACULA_LABEL;
    if (is_tape() || is_fifo()) {
       open_tape_device(dcr, omode);
@@ -349,6 +406,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    utime_t start_time = time(NULL);
 #endif
 
+   mount(1);                          /* do mount if required */
 
    Dmsg0(100, "Open dev: device is tape\n");
 
@@ -369,7 +427,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
 #if defined(HAVE_WIN32)
 
    /*   Windows Code */
-   if ((m_fd = tape_open(dev_name, mode)) < 0) {
+   if ((m_fd = d_open(dev_name, mode)) < 0) {
       dev_errno = errno;
    }
 
@@ -379,7 +437,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    /* If busy retry each second for max_open_wait seconds */
    for ( ;; ) {
       /* Try non-blocking open */
-      m_fd = ::open(dev_name, mode+O_NONBLOCK);
+      m_fd = d_open(dev_name, mode+O_NONBLOCK);
       if (m_fd < 0) {
          berrno be;
          dev_errno = errno;
@@ -391,10 +449,10 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
          mt_com.mt_op = MTREW;
          mt_com.mt_count = 1;
          /* rewind only if dev is a tape */
-         if (is_tape() && (ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0)) {
+         if (is_tape() && (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0)) {
             berrno be;
             dev_errno = errno;           /* set error status from rewind */
-            ::close(m_fd);
+            d_close(m_fd);
             clear_opened();
             Dmsg2(100, "Rewind error on %s close: ERR=%s\n", print_name(),
                   be.bstrerror(dev_errno));
@@ -404,8 +462,8 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
             }
          } else {
             /* Got fd and rewind worked, so we must have medium in drive */
-            ::close(m_fd);
-            m_fd = ::open(dev_name, mode);  /* open normally */
+            d_close(m_fd);
+            m_fd = d_open(dev_name, mode);  /* open normally */
             if (m_fd < 0) {
                berrno be;
                dev_errno = errno;
@@ -473,7 +531,7 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
       if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
          pm_strcat(archive_name, "/");
       }
-      pm_strcat(archive_name, VolCatInfo.VolCatName);
+      pm_strcat(archive_name, getVolCatName());
    }
 
    mount(1);                          /* do mount if required */
@@ -490,7 +548,7 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
       Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), 
             be.bstrerror());
       Dmsg1(100, "open failed: %s", errmsg);
-      Emsg0(M_FATAL, 0, errmsg);
+//    Jmsg1(NULL, M_WARNING, 0, "%s", errmsg);
    } else {
       dev_errno = 0;
       file = 0;
@@ -501,8 +559,8 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
 }
 
 /*
- * Open a DVD device. N.B. at this point, dcr->VolCatInfo.VolCatName 
- *  (NB:??? I think it's VolCatInfo.VolCatName that is right)
+ * Open a DVD device. N.B. at this point, dcr->getVolCatName() 
+ *  (NB:??? I think it's getVolCatName() that is right)
  *  has the desired Volume name, but there is NO assurance that
  *  any other field of VolCatInfo is correct.
  */
@@ -526,7 +584,7 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
     *  copy here, when opening.
     */
    VolCatInfo = dcr->VolCatInfo;         /* structure assignment */
-   Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName);
+   Dmsg1(100, "Volume=%s\n", getVolCatName());
 
    if (VolCatInfo.VolCatName[0] == 0) {
       Dmsg1(10,  "Could not open DVD device %s. No Volume name given.\n",
@@ -689,7 +747,7 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
                be.bstrerror());
          Dmsg1(100, "open failed: %s", errmsg);
          /* Use system close() */
-         ::close(m_fd);
+         d_close(m_fd);
          clear_opened();
       } else {
          part_size = filestat.st_size;
@@ -711,7 +769,7 @@ bool DEVICE::rewind(DCR *dcr)
    unsigned int i;
    bool first = true;
 
-   Dmsg3(400, "rewind res=%d fd=%d %s\n", reserved_device, m_fd, print_name());
+   Dmsg3(400, "rewind res=%d fd=%d %s\n", num_reserved(), m_fd, print_name());
    state &= ~(ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
    block_num = file = 0;
    file_size = 0;
@@ -733,7 +791,7 @@ bool DEVICE::rewind(DCR *dcr)
        * retrying every 5 seconds.
        */
       for (i=max_rewind_wait; ; i -= 5) {
-         if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
             berrno be;
             clrerror(MTREW);
             if (i == max_rewind_wait) {
@@ -747,7 +805,7 @@ bool DEVICE::rewind(DCR *dcr)
              */
             if (first && dcr) {
                int open_mode = openmode;
-               tape_close(m_fd);
+               d_close(m_fd);
                clear_opened();
                open(dcr, open_mode);
                if (m_fd < 0) {
@@ -835,7 +893,7 @@ bool DEVICE::eod(DCR *dcr)
    return fsf(VolCatInfo.VolCatFiles);
 #endif
 
-   Dmsg0(100, "eod\n");
+   Dmsg0(100, "Enter eod\n");
    if (at_eot()) {
       return true;
    }
@@ -843,12 +901,12 @@ bool DEVICE::eod(DCR *dcr)
    block_num = file = 0;
    file_size = 0;
    file_addr = 0;
-   if (is_fifo() || is_prog()) {
+   if (is_fifo()) {
       return true;
    }
    if (!is_tape()) {
       pos = lseek(dcr, (boffset_t)0, SEEK_END);
-//    Dmsg1(100, "====== Seek to %lld\n", pos);
+      Dmsg1(200, "====== Seek to %lld\n", pos);
       if (pos >= 0) {
          update_pos(dcr);
          set_eot();
@@ -858,6 +916,7 @@ bool DEVICE::eod(DCR *dcr)
       berrno be;
       Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
              print_name(), be.bstrerror());
+      Dmsg0(100, errmsg);
       return false;
    }
 #ifdef MTEOM
@@ -866,6 +925,7 @@ bool DEVICE::eod(DCR *dcr)
       /* If unknown position, rewind */
       if (get_os_tape_file() < 0) {
         if (!rewind(NULL)) {
+          Dmsg0(100, "Rewind error\n");
           return false;
         }
       }
@@ -887,13 +947,14 @@ bool DEVICE::eod(DCR *dcr)
          mt_com.mt_count = 1;
       }
 
-      if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
+      if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
          berrno be;
          clrerror(mt_com.mt_op);
          Dmsg1(50, "ioctl error: %s\n", be.bstrerror());
          update_pos(dcr);
          Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"),
             print_name(), be.bstrerror());
+         Dmsg0(100, errmsg);
          return false;
       }
 
@@ -903,6 +964,7 @@ bool DEVICE::eod(DCR *dcr)
          clrerror(-1);
          Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"),
             print_name(), be.bstrerror());
+         Dmsg0(100, errmsg);
          return false;
       }
       Dmsg1(100, "EOD file=%d\n", os_file);
@@ -916,6 +978,7 @@ bool DEVICE::eod(DCR *dcr)
        * Rewind then use FSF until EOT reached
        */
       if (!rewind(NULL)) {
+         Dmsg0(100, "Rewind error.\n");
          return false;
       }
       /*
@@ -925,7 +988,7 @@ bool DEVICE::eod(DCR *dcr)
       for (file_num=file; !at_eot(); file_num++) {
          Dmsg0(200, "eod: doing fsf 1\n");
          if (!fsf(1)) {
-            Dmsg0(200, "fsf error.\n");
+            Dmsg0(100, "fsf error.\n");
             return false;
          }
          /*
@@ -1031,7 +1094,7 @@ uint32_t status_dev(DEVICE *dev)
       stat |= BMT_TAPE;
       Pmsg0(-20,_(" Bacula status:"));
       Pmsg2(-20,_(" file=%d block=%d\n"), dev->file, dev->block_num);
-      if (tape_ioctl(dev->fd(), MTIOCGET, (char *)&mt_stat) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCGET, (char *)&mt_stat) < 0) {
          berrno be;
          dev->dev_errno = errno;
          Mmsg2(dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"),
@@ -1158,7 +1221,7 @@ bool load_dev(DEVICE *dev)
    dev->file_addr = 0;
    mt_com.mt_op = MTLOAD;
    mt_com.mt_count = 1;
-   if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+   if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
       berrno be;
       dev->dev_errno = errno;
       Mmsg2(dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"),
@@ -1189,7 +1252,7 @@ bool DEVICE::offline()
    unlock_door();
    mt_com.mt_op = MTOFFL;
    mt_com.mt_count = 1;
-   if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
+   if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) {
       berrno be;
       dev_errno = errno;
       Mmsg2(errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"),
@@ -1264,7 +1327,7 @@ bool DEVICE::fsf(int num)
       int my_errno = 0;
       mt_com.mt_op = MTFSF;
       mt_com.mt_count = num;
-      stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+      stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
       if (stat < 0) {
          my_errno = errno;            /* save errno */
       } else if ((os_file=get_os_tape_file()) < 0) {
@@ -1345,7 +1408,7 @@ bool DEVICE::fsf(int num)
          }
 
          Dmsg0(100, "Doing MTFSF\n");
-         stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+         stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
          if (stat < 0) {                 /* error => EOT */
             berrno be;
             set_eot();
@@ -1419,7 +1482,7 @@ bool DEVICE::bsf(int num)
    file_size = 0;
    mt_com.mt_op = MTBSF;
    mt_com.mt_count = num;
-   stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
    if (stat < 0) {
       berrno be;
       clrerror(MTBSF);
@@ -1459,7 +1522,7 @@ bool DEVICE::fsr(int num)
    Dmsg1(100, "fsr %d\n", num);
    mt_com.mt_op = MTFSR;
    mt_com.mt_count = num;
-   stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
    if (stat == 0) {
       clear_eof();
       block_num += num;
@@ -1518,7 +1581,7 @@ bool DEVICE::bsr(int num)
    clear_eot();
    mt_com.mt_op = MTBSR;
    mt_com.mt_count = num;
-   stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
    if (stat < 0) {
       berrno be;
       clrerror(MTBSR);
@@ -1534,7 +1597,7 @@ void DEVICE::lock_door()
    struct mtop mt_com;
    mt_com.mt_op = MTLOCK;
    mt_com.mt_count = 1;
-   tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
 #endif
 }
 
@@ -1544,9 +1607,22 @@ void DEVICE::unlock_door()
    struct mtop mt_com;
    mt_com.mt_op = MTUNLOCK;
    mt_com.mt_count = 1;
-   tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
 #endif
 }
+
+void DEVICE::set_slot(int32_t slot)
+{ 
+   m_slot = slot; 
+   if (vol) vol->clear_slot();
+}
+
+void DEVICE::clear_slot()
+{ 
+   m_slot = -1; 
+   if (vol) vol->set_slot(-1);
+}
+
  
 
 /*
@@ -1633,7 +1709,7 @@ bool DEVICE::weof(int num)
 {
    struct mtop mt_com;
    int stat;
-   Dmsg0(129, "weof_dev\n");
+   Dmsg1(129, "=== weof_dev=%s\n", print_name());
    
    if (!is_open()) {
       dev_errno = EBADF;
@@ -1656,7 +1732,7 @@ bool DEVICE::weof(int num)
    clear_eot();
    mt_com.mt_op = MTWEOF;
    mt_com.mt_count = num;
-   stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   stat = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
    if (stat == 0) {
       block_num = 0;
       file += num;
@@ -1787,7 +1863,7 @@ void DEVICE::clrerror(int func)
 /* Found on Solaris */
 #ifdef MTIOCLRERR
 {
-   tape_ioctl(m_fd, MTIOCLRERR);
+   d_ioctl(m_fd, MTIOCLRERR);
    Dmsg0(200, "Did MTIOCLRERR\n");
 }
 #endif
@@ -1800,7 +1876,7 @@ void DEVICE::clrerror(int func)
    union mterrstat mt_errstat;
    Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev_errno,
       be.bstrerror(dev_errno));
-   tape_ioctl(m_fd, MTIOCERRSTAT, (char *)&mt_errstat);
+   d_ioctl(m_fd, MTIOCERRSTAT, (char *)&mt_errstat);
 }
 #endif
 
@@ -1811,13 +1887,25 @@ void DEVICE::clrerror(int func)
    mt_com.mt_op = MTCSE;
    mt_com.mt_count = 1;
    /* Clear any error condition on the tape */
-   tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
+   d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com);
    Dmsg0(200, "Did MTCSE\n");
 }
 #endif
 }
 
 
+/*
+ * Set to unload the current volume in the drive
+ */
+void DEVICE::set_unload()
+{
+   if (!m_unload && VolHdr.VolumeName[0] != 0) {
+       m_unload = true;
+       memcpy(UnloadVolName, VolHdr.VolumeName, sizeof(UnloadVolName));
+   }
+}
+
+
 /*
  * Clear volume header
  */
@@ -1825,6 +1913,7 @@ void DEVICE::clear_volhdr()
 {
    Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName);
    memset(&VolHdr, 0, sizeof(VolHdr));
+   setVolCatInfo(false);
 }
 
 
@@ -1834,9 +1923,7 @@ void DEVICE::clear_volhdr()
 void DEVICE::close()
 {
    Dmsg1(100, "close_dev %s\n", print_name());
-   if (has_cap(CAP_OFFLINEUNMOUNT)) {
-      offline();
-   }
+   offline_or_rewind();
 
    if (!is_open()) {
       Dmsg2(100, "device %s already closed vol=%s\n", print_name(),
@@ -1845,25 +1932,31 @@ void DEVICE::close()
    }
 
    switch (dev_type) {
+   case B_VTL_DEV:
+   case B_VTAPE_DEV:
    case B_TAPE_DEV:
       unlock_door(); 
-      tape_close(m_fd);
-      break;
    default:
-      ::close(m_fd);
+      d_close(m_fd);
+      break;
    }
 
+   unmount(1);                        /* do unmount if required */
+
    /* Clean up device packet so it can be reused */
    clear_opened();
+   /*
+    * Be careful not to clear items needed by the DVD driver
+    *    when it is closing a single part.
+    */
    state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF|
-              ST_MOUNTED|ST_MEDIA|ST_SHORT|ST_FREESPACE_OK|ST_PART_SPOOLED);
+              ST_MOUNTED|ST_MEDIA|ST_SHORT);
    label_type = B_BACULA_LABEL;
    file = block_num = 0;
    file_size = 0;
    file_addr = 0;
    EndFile = EndBlock = 0;
    openmode = 0;
-   Slot = -1;             /* unknown slot */
    clear_volhdr();
    memset(&VolCatInfo, 0, sizeof(VolCatInfo));
    if (tid) {
@@ -1881,7 +1974,7 @@ void DEVICE::close()
  *  the Volume parts multiple times without losing track of what the    
  *  main Volume parameters are.
  */
-void DEVICE::close_part(DCR *dcr)
+void DEVICE::close_part(DCR * /*dcr*/)
 {
    VOLUME_LABEL saveVolHdr;
    VOLUME_CAT_INFO saveVolCatInfo;     /* Volume Catalog Information */
@@ -1892,7 +1985,6 @@ void DEVICE::close_part(DCR *dcr)
    close();                           /* close current part */
    VolHdr = saveVolHdr;               /* structure assignment */
    VolCatInfo = saveVolCatInfo;       /* structure assignment */
-   dcr->VolCatInfo = saveVolCatInfo;  /* structure assignment */
 }
 
 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
@@ -1904,7 +1996,7 @@ boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
 #if defined(HAVE_WIN32)
       return ::_lseeki64(m_fd, (__int64)offset, whence);
 #else
-      return ::lseek(m_fd, (off_t)offset, whence);
+      return ::lseek(m_fd, offset, whence);
 #endif
    }
    return -1;
@@ -1913,97 +2005,238 @@ boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
 
 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
 {
+   struct stat st;
+
    Dmsg1(100, "truncate %s\n", print_name());
    switch (dev_type) {
+   case B_VTL_DEV:
+   case B_VTAPE_DEV:
    case B_TAPE_DEV:
       /* maybe we should rewind and write and eof ???? */
       return true;                    /* we don't really truncate tapes */
    case B_DVD_DEV:
       return truncate_dvd(dcr);
    case B_FILE_DEV:
-      /* ***FIXME*** we really need to unlink() the file so that
-       *  its name can be changed for a relabel.
-       */
       if (ftruncate(m_fd, 0) != 0) {
          berrno be;
          Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), 
                print_name(), be.bstrerror());
          return false;
       }
+          
+      /*
+       * Check for a successful ftruncate() and issue a work-around for devices 
+       * (mostly cheap NAS) that don't support truncation. 
+       * Workaround supplied by Martin Schmid as a solution to bug #1011.
+       * 1. close file
+       * 2. delete file
+       * 3. open new file with same mode
+       * 4. change ownership to original
+       */
+
+      if (fstat(m_fd, &st) != 0) {
+         berrno be;
+         Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), 
+               print_name(), be.bstrerror());
+         return false;
+      }
+          
+      if (st.st_size != 0) {             /* ftruncate() didn't work */
+         POOL_MEM archive_name(PM_FNAME);
+                
+         pm_strcpy(archive_name, dev_name);
+         if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
+            pm_strcat(archive_name, "/");
+         }
+         pm_strcat(archive_name, dcr->VolumeName);
+                   
+         Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"), 
+               print_name(), archive_name.c_str());
+
+         /* Close file and blow it away */
+         ::close(m_fd);
+         ::unlink(archive_name.c_str());
+                   
+         /* Recreate the file -- of course, empty */
+         set_mode(CREATE_READ_WRITE);
+         if ((m_fd = ::open(archive_name.c_str(), mode, st.st_mode)) < 0) {
+            berrno be;
+            dev_errno = errno;
+            Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(), 
+                  be.bstrerror());
+            Dmsg1(100, "reopen failed: %s", errmsg);
+            Emsg0(M_FATAL, 0, errmsg);
+            return false;
+         }
+                   
+         /* Reset proper owner */
+         chown(archive_name.c_str(), st.st_uid, st.st_gid);  
+      }
+          
       return true;
    }
    return false;
 }
 
-/* Mount the device.
+/*
+ * Mount the device.
  * If timeout, wait until the mount command returns 0.
  * If !timeout, try to mount the device only once.
  */
 bool DEVICE::mount(int timeout) 
 {
    Dmsg0(190, "Enter mount\n");
+
    if (is_mounted()) {
       return true;
-   } else if (requires_mount()) {
-      return do_mount(1, timeout);
-   }       
+   }
+
+   switch (dev_type) {
+   case B_VTL_DEV:
+   case B_VTAPE_DEV:
+   case B_TAPE_DEV:
+      if (device->mount_command) {
+         return do_tape_mount(1, timeout);
+      }
+      break;
+   case B_FILE_DEV:
+   case B_DVD_DEV:
+      if (requires_mount() && device->mount_command) {
+         return do_file_mount(1, timeout);
+      }
+      break;
+   default:
+      break;
+   }
+
    return true;
 }
 
-/* Unmount the device
+/*
+ * Unmount the device
  * If timeout, wait until the unmount command returns 0.
  * If !timeout, try to unmount the device only once.
  */
 bool DEVICE::unmount(int timeout) 
 {
    Dmsg0(100, "Enter unmount\n");
-   if (is_mounted()) {
-      return do_mount(0, timeout);
+
+   if (!is_mounted()) {
+      return true;
+   }
+
+   switch (dev_type) {
+   case B_VTL_DEV:
+   case B_VTAPE_DEV:
+   case B_TAPE_DEV:
+      if (device->unmount_command) {
+         return do_tape_mount(0, timeout);
+      }
+      break;
+   case B_FILE_DEV:
+   case B_DVD_DEV:
+      if (requires_mount() && device->unmount_command) {
+         return do_file_mount(0, timeout);
+      }
+      break;
+   default:
+      break;
    }
+
    return true;
 }
 
-/* (Un)mount the device */
-bool DEVICE::do_mount(int mount, int dotimeout) 
+/*
+ * (Un)mount the device (for tape devices)
+ */
+bool DEVICE::do_tape_mount(int mount, int dotimeout)
 {
    POOL_MEM ocmd(PM_FNAME);
    POOLMEM *results;
    char *icmd;
-   int status, timeout;
-   
+   int status, tries;
+   berrno be;
+
    Dsm_check(1);
    if (mount) {
-      if (is_mounted()) {
-         Dmsg0(200, "======= mount=1\n");
-         return true;
-      }
       icmd = device->mount_command;
    } else {
-      if (!is_mounted()) {
-         Dmsg0(200, "======= mount=0\n");
-         return true;
+      icmd = device->unmount_command;
+   }
+
+   edit_mount_codes(ocmd, icmd);
+
+   Dmsg2(100, "do_tape_mount: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
+
+   if (dotimeout) {
+      /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
+      tries = 10;
+   } else {
+      tries = 1;
+   }
+   results = get_memory(4000);
+
+   /* If busy retry each second */
+   Dmsg1(100, "do_tape_mount run_prog=%s\n", ocmd.c_str());
+   while ((status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results)) != 0) {
+      if (tries-- > 0) {
+         continue;
       }
+
+      Dmsg5(100, "Device %s cannot be %smounted. stat=%d result=%s ERR=%s\n", print_name(),
+           (mount ? "" : "un"), status, results, be.bstrerror(status));
+      Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"),
+           print_name(), (mount ? "" : "un"), be.bstrerror(status));
+
+      set_mounted(false);
+      free_pool_memory(results);
+      Dmsg0(200, "============ mount=0\n");
+      Dsm_check(1);
+      return false;
+   }
+
+   set_mounted(mount);              /* set/clear mounted flag */
+   free_pool_memory(results);
+   Dmsg1(200, "============ mount=%d\n", mount);
+   return true;
+}
+
+/*
+ * (Un)mount the device (either a FILE or DVD device)
+ */
+bool DEVICE::do_file_mount(int mount, int dotimeout) 
+{
+   POOL_MEM ocmd(PM_FNAME);
+   POOLMEM *results;
+   DIR* dp;
+   char *icmd;
+   struct dirent *entry, *result;
+   int status, tries, name_max, count;
+   berrno be;
+
+   Dsm_check(1);
+   if (mount) {
+      icmd = device->mount_command;
+   } else {
       icmd = device->unmount_command;
    }
    
    clear_freespace_ok();
    edit_mount_codes(ocmd, icmd);
    
-   Dmsg2(100, "do_mount: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
+   Dmsg2(100, "do_file_mount: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted());
 
    if (dotimeout) {
-      /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */
-      timeout = 1;
+      /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
+      tries = 10;
    } else {
-      timeout = 0;
+      tries = 1;
    }
    results = get_memory(4000);
-   results[0] = 0;
 
    /* If busy retry each second */
-   Dmsg1(100, "do_mount run_prog=%s\n", ocmd.c_str());
-   while ((status = run_program_full_output(ocmd.c_str(), 
-                       max_open_wait/2, results)) != 0) {
+   Dmsg1(100, "do_file_mount run_prog=%s\n", ocmd.c_str());
+   while ((status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results)) != 0) {
       /* Doesn't work with internationalization (This is not a problem) */
       if (mount && fnmatch("*is already mounted on*", results, 0) == 0) {
          break;
@@ -2011,37 +2244,24 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
          break;
       }
-      if (timeout-- > 0) {
+      if (tries-- > 0) {
          /* Sometimes the device cannot be mounted because it is already mounted.
           * Try to unmount it, then remount it */
          if (mount) {
             Dmsg1(400, "Trying to unmount the device %s...\n", print_name());
-            do_mount(0, 0);
+            do_file_mount(0, 0);
          }
          bmicrosleep(1, 0);
          continue;
       }
-      if (status != 0) {
-         berrno be;
-         Dmsg5(100, "Device %s cannot be %smounted. stat=%d result=%s ERR=%s\n", print_name(),
-              (mount ? "" : "un"), status, results, be.bstrerror(status));
-         Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"), 
-              print_name(), (mount ? "" : "un"), be.bstrerror(status));
-      } else {
-         Dmsg4(100, "Device %s cannot be %smounted. stat=%d ERR=%s\n", print_name(),
-              (mount ? "" : "un"), status, results);
-         Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"), 
-              print_name(), (mount ? "" : "un"), results);
-      }
+      Dmsg5(100, "Device %s cannot be %smounted. stat=%d result=%s ERR=%s\n", print_name(),
+           (mount ? "" : "un"), status, results, be.bstrerror(status));
+      Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"),
+           print_name(), (mount ? "" : "un"), be.bstrerror(status));
+
       /*
-       * Now, just to be sure it is not mounted, try to read the
-       *  filesystem.
+       * Now, just to be sure it is not mounted, try to read the filesystem.
        */
-      DIR* dp;
-      struct dirent *entry, *result;
-      int name_max;
-      int count;
-      
       name_max = pathconf(".", _PC_NAME_MAX);
       if (name_max < 1024) {
          name_max = 1024;
@@ -2050,7 +2270,7 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       if (!(dp = opendir(device->mount_point))) {
          berrno be;
          dev_errno = errno;
-         Dmsg3(100, "do_mount: failed to open dir %s (dev=%s), ERR=%s\n", 
+         Dmsg3(100, "do_file_mount: failed to open dir %s (dev=%s), ERR=%s\n", 
                device->mount_point, print_name(), be.bstrerror());
          goto get_out;
       }
@@ -2060,7 +2280,7 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       while (1) {
          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
             dev_errno = EIO;
-            Dmsg2(129, "do_mount: failed to find suitable file in dir %s (dev=%s)\n", 
+            Dmsg2(129, "do_file_mount: failed to find suitable file in dir %s (dev=%s)\n", 
                   device->mount_point, print_name());
             break;
          }
@@ -2068,13 +2288,13 @@ bool DEVICE::do_mount(int mount, int dotimeout)
             count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
             break;
          } else {
-            Dmsg2(129, "do_mount: ignoring %s in %s\n", result->d_name, device->mount_point);
+            Dmsg2(129, "do_file_mount: ignoring %s in %s\n", result->d_name, device->mount_point);
          }
       }
       free(entry);
       closedir(dp);
       
-      Dmsg1(100, "do_mount: got %d files in the mount point (not counting ., .. and .keep)\n", count);
+      Dmsg1(100, "do_file_mount: got %d files in the mount point (not counting ., .. and .keep)\n", count);
       
       if (count > 0) {
          /* If we got more than ., .. and .keep */
@@ -2180,12 +2400,15 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg)
    }
 }
 
-/* return the last timer interval (ms) */
+/* return the last timer interval (ms) 
+ * or 0 if something goes wrong
+ */
 btime_t DEVICE::get_timer_count()
 {
-   btime_t old = last_timer;
+   btime_t temp = last_timer;
    last_timer = get_current_btime();
-   return last_timer - old;
+   temp = last_timer - temp;   /* get elapsed time */
+   return (temp>0)?temp:0;     /* take care of skewed clock */
 }
 
 /* read from fd */
@@ -2195,11 +2418,7 @@ ssize_t DEVICE::read(void *buf, size_t len)
 
    get_timer_count();
 
-   if (this->is_tape()) {
-      read_len = tape_read(m_fd, buf, len);
-   } else {
-      read_len = ::read(m_fd, buf, len);
-   }
+   read_len = d_read(m_fd, buf, len);
 
    last_tick = get_timer_count();
 
@@ -2220,11 +2439,7 @@ ssize_t DEVICE::write(const void *buf, size_t len)
 
    get_timer_count();
 
-   if (this->is_tape()) {
-      write_len = tape_write(m_fd, buf, len);
-   } else {
-      write_len = ::write(m_fd, buf, len);
-   }
+   write_len = d_write(m_fd, buf, len);
 
    last_tick = get_timer_count();
 
@@ -2250,7 +2465,7 @@ int32_t DEVICE::get_os_tape_file()
    struct mtget mt_stat;
 
    if (has_cap(CAP_MTIOCGET) &&
-       tape_ioctl(m_fd, MTIOCGET, (char *)&mt_stat) == 0) {
+       d_ioctl(m_fd, MTIOCGET, (char *)&mt_stat) == 0) {
       return mt_stat.mt_fileno;
    }
    return -1;
@@ -2259,7 +2474,7 @@ int32_t DEVICE::get_os_tape_file()
 char *
 dev_vol_name(DEVICE *dev)
 {
-   return dev->VolCatInfo.VolCatName;
+   return dev->getVolCatName();
 }
 
 
@@ -2313,7 +2528,6 @@ void init_device_wait_timers(DCR *dcr)
    dev->rem_wait_sec = dev->wait_sec;
    dev->num_wait = 0;
    dev->poll = false;
-   dev->BadVolName[0] = 0;
 
    jcr->min_wait = 60 * 60;
    jcr->max_wait = 24 * 60 * 60;
@@ -2375,13 +2589,13 @@ void set_os_device_parameters(DCR *dcr)
       mt_com.mt_op = MTSETBLK;
       mt_com.mt_count = 0;
       Dmsg0(100, "Set block size to zero\n");
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTSETBLK);
       }
    }
 #endif
 #if defined(MTSETDRVBUFFER)
-   if (getpid() == 0) {          /* Only root can do this */
+   if (getuid() == 0) {          /* Only root can do this */
       mt_com.mt_op = MTSETDRVBUFFER;
       mt_com.mt_count = MT_ST_CLEARBOOLEANS;
       if (!dev->has_cap(CAP_TWOEOF)) {
@@ -2391,7 +2605,7 @@ void set_os_device_parameters(DCR *dcr)
          mt_com.mt_count |= MT_ST_FAST_MTEOM;
       }
       Dmsg0(100, "MTSETDRVBUFFER\n");
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTSETDRVBUFFER);
       }
    }
@@ -2405,13 +2619,13 @@ void set_os_device_parameters(DCR *dcr)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSETBSIZ;
       mt_com.mt_count = 0;
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTSETBSIZ);
       }
       /* Get notified at logical end of tape */
       mt_com.mt_op = MTEWARN;
       mt_com.mt_count = 1;
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTEWARN);
       }
    }
@@ -2424,7 +2638,7 @@ void set_os_device_parameters(DCR *dcr)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSETBSIZ;
       mt_com.mt_count = 0;
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTSETBSIZ);
       }
    }
@@ -2435,7 +2649,7 @@ void set_os_device_parameters(DCR *dcr)
    } else {
       neof = 1;
    }
-   if (ioctl(dev->fd(), MTIOCSETEOTMODEL, (caddr_t)&neof) < 0) {
+   if (dev->d_ioctl(dev->fd(), MTIOCSETEOTMODEL, (caddr_t)&neof) < 0) {
       berrno be;
       dev->dev_errno = errno;         /* save errno */
       Mmsg2(dev->errmsg, _("Unable to set eotmodel on device %s: ERR=%s\n"),
@@ -2452,7 +2666,7 @@ void set_os_device_parameters(DCR *dcr)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSRSZ;
       mt_com.mt_count = 0;
-      if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
+      if (dev->d_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) {
          dev->clrerror(MTSRSZ);
       }
    }
@@ -2464,11 +2678,11 @@ static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat)
 {
    Dmsg0(100, "dev_get_os_pos\n");
    return dev->has_cap(CAP_MTIOCGET) && 
-          tape_ioctl(dev->fd(), MTIOCGET, (char *)mt_stat) == 0 &&
+          dev->d_ioctl(dev->fd(), MTIOCGET, (char *)mt_stat) == 0 &&
           mt_stat->mt_fileno >= 0;
 }
 
-static char *modes[] = {
+static const char *modes[] = {
    "CREATE_READ_WRITE",
    "OPEN_READ_WRITE",
    "OPEN_READ_ONLY",
@@ -2476,7 +2690,7 @@ static char *modes[] = {
 };
 
 
-static char *mode_to_str(int mode)  
+static const char *mode_to_str(int mode)  
 {
    static char buf[100];
    if (mode < 1 || mode > 4) {