]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/dev.c
Make bad Storage check in is_on_same_storage non-fatal
[bacula/bacula] / bacula / src / stored / dev.c
index 98aa584f7202852585a76f36c51dcf14831e329c..7a3dafe03dbaca7f7a8c594c4d57243c5f23f530 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2009 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.
    This program is Free Software; you can redistribute it and/or
 
    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
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
    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
+   You should have received a copy of the GNU Affero 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.
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
  *     daemon. More complicated coding (double buffering, writer
  *     thread, ...) is left for a later version.
  *
  *     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.
  */
 
 /*
  * 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
  * 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
@@ -143,9 +142,36 @@ init_dev(JCR *jcr, DEVRES *device)
          device->dev_type = B_DVD_DEV;
       }
    }
          device->dev_type = B_DVD_DEV;
       }
    }
-
-   dev = (DEVICE *)malloc(sizeof(DEVICE));
-   memset(dev, 0, sizeof(DEVICE));
+   switch (device->dev_type) {
+   case B_DVD_DEV:
+      Jmsg0(jcr, M_FATAL, 0, _("DVD support is now deprecated\n"));
+      return NULL;
+   case B_VTAPE_DEV:
+      dev = New(vtape);
+      break;
+#ifdef USE_FTP
+   case B_FTP_DEV:
+      dev = New(ftp_device);
+      break;
+#endif
+#ifdef HAVE_WIN32
+/* TODO: defined in src/win32/stored/mtops.cpp */
+   case B_TAPE_DEV:
+      dev = New(win32_tape_device);
+      break;
+   case B_FILE_DEV:
+      dev = New(win32_file_device);
+      break;
+#else
+   case B_TAPE_DEV:
+   case B_FILE_DEV:
+   case B_FIFO_DEV:
+      dev = New(DEVICE);
+      break;
+#endif
+   default:
+         return NULL;
+   }
    dev->clear_slot();         /* unknown */
 
    /* Copy user supplied device parameters from Resource */
    dev->clear_slot();         /* unknown */
 
    /* Copy user supplied device parameters from Resource */
@@ -160,6 +186,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_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;
    dev->volume_capacity = device->volume_capacity;
    dev->max_rewind_wait = device->max_rewind_wait;
    dev->max_open_wait = device->max_open_wait;
@@ -169,7 +196,6 @@ init_dev(JCR *jcr, DEVRES *device)
    dev->drive_index = device->drive_index;
    dev->autoselect = device->autoselect;
    dev->dev_type = device->dev_type;
    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 {
    if (dev->is_tape()) { /* No parts on tapes */
       dev->max_part_size = 0;
    } else {
@@ -191,21 +217,17 @@ init_dev(JCR *jcr, DEVRES *device)
     * - Check that the mount point is available 
     * - Check that (un)mount commands are defined
     */
     * - Check that the mount point is available 
     * - Check that (un)mount commands are defined
     */
-   if ((dev->is_file() || dev->is_dvd()) && dev->requires_mount()) {
+   if (dev->is_file() && dev->requires_mount()) {
       if (!device->mount_point || stat(device->mount_point, &statp) < 0) {
          berrno be;
          dev->dev_errno = errno;
          Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"), 
             device->mount_point, be.bstrerror());
       }
       if (!device->mount_point || stat(device->mount_point, &statp) < 0) {
          berrno be;
          dev->dev_errno = errno;
          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 (!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 (!device->write_part_command) {
-         Jmsg0(jcr, M_ERROR_TERM, 0, _("Write part command must be defined for a device which requires mount.\n"));
-      }
    }
 
    /* Sanity check */
    }
 
    /* Sanity check */
@@ -227,6 +249,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());
    }
       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;
 
    dev->errmsg = get_pool_memory(PM_EMSG);
    *dev->errmsg = 0;
@@ -255,6 +281,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);
    }
       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;
 #ifdef xxx
    if ((errstat = rwl_init(&dev->lock)) != 0) {
       berrno be;
@@ -272,42 +308,30 @@ init_dev(JCR *jcr, DEVRES *device)
    return dev;
 }
 
    return dev;
 }
 
-/* Choose the right backend */
-void DEVICE::init_backend()
+/* default primitives are designed for file */
+int DEVICE::d_open(const char *pathname, int flags)
 {
 {
+   return ::open(pathname, flags);
+}
 
 
-#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;
+int DEVICE::d_close(int fd)
+{
+   return ::close(fd);
+}
 
 
-   } 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
+int DEVICE::d_ioctl(int fd, ioctl_req_t request, char *mt_com)
+{
+   return ::ioctl(fd, request, mt_com);
+}
+
+ssize_t DEVICE::d_read(int fd, void *buffer, size_t count)
+{
+   return ::read(fd, buffer, count);
+}
+
+ssize_t DEVICE::d_write(int fd, const void *buffer, size_t count)
+{
+   return ::write(fd, buffer, count);
 }
 
 /*
 }
 
 /*
@@ -338,18 +362,19 @@ DEVICE::open(DCR *dcr, int omode)
       }
    }
    if (dcr) {
       }
    }
    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,
    }
 
    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);
    label_type = B_BACULA_LABEL;
    state &= ~(ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF);
    label_type = B_BACULA_LABEL;
+
    if (is_tape() || is_fifo()) {
       open_tape_device(dcr, omode);
    if (is_tape() || is_fifo()) {
       open_tape_device(dcr, omode);
-   } else if (is_dvd()) {
-      Dmsg1(100, "call open_dvd_device mode=%s\n", mode_to_str(omode));
-      open_dvd_device(dcr, omode);
+   } else if (is_ftp()) {
+      open_device(dcr, omode);
    } else {
       Dmsg1(100, "call open_file_device mode=%s\n", mode_to_str(omode));
       open_file_device(dcr, omode);
    } else {
       Dmsg1(100, "call open_file_device mode=%s\n", mode_to_str(omode));
       open_file_device(dcr, omode);
@@ -390,6 +415,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    utime_t start_time = time(NULL);
 #endif
 
    utime_t start_time = time(NULL);
 #endif
 
+   mount(1);                          /* do mount if required */
 
    Dmsg0(100, "Open dev: device is tape\n");
 
 
    Dmsg0(100, "Open dev: device is tape\n");
 
@@ -483,6 +509,10 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    Dmsg1(100, "open dev: tape %d opened\n", m_fd);
 }
 
    Dmsg1(100, "open dev: tape %d opened\n", m_fd);
 }
 
+void DEVICE::open_device(DCR *dcr, int omode)
+{
+   /* do nothing waiting to split open_file/tape_device */
+}
 
 /*
  * Open a file device
 
 /*
  * Open a file device
@@ -514,7 +544,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, "/");
       }
       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 */
    }
 
    mount(1);                          /* do mount if required */
@@ -531,7 +561,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);
       Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), 
             be.bstrerror());
       Dmsg1(100, "open failed: %s", errmsg);
-      Jmsg1(NULL, M_WARNING, 0, "%s", errmsg);
+//    Jmsg1(NULL, M_WARNING, 0, "%s", errmsg);
    } else {
       dev_errno = 0;
       file = 0;
    } else {
       dev_errno = 0;
       file = 0;
@@ -541,206 +571,6 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
       m_fd, part, num_dvd_parts, part_size);
 }
 
       m_fd, part, num_dvd_parts, part_size);
 }
 
-/*
- * Open a DVD device. N.B. at this point, dcr->VolCatInfo.VolCatName 
- *  (NB:??? I think it's VolCatInfo.VolCatName that is right)
- *  has the desired Volume name, but there is NO assurance that
- *  any other field of VolCatInfo is correct.
- */
-void DEVICE::open_dvd_device(DCR *dcr, int omode) 
-{
-   POOL_MEM archive_name(PM_FNAME);
-   struct stat filestat;
-
-   /*
-    * Handle opening of DVD Volume
-    */     
-   Dmsg2(100, "Enter: open_dvd_dev: DVD vol=%s mode=%s\n", 
-         &dcr->VolCatInfo, mode_to_str(omode));
-
-   /*
-    * For a DVD we must always pull the state info from dcr->VolCatInfo
-    *  This is a bit ugly, but is necessary because we need to open/close/re-open
-    *  the dvd file in order to properly mount/unmount and access the
-    *  DVD. So we store the state of the DVD as far as is known in the 
-    *  catalog in dcr->VolCatInfo, and thus we refresh the dev->VolCatInfo
-    *  copy here, when opening.
-    */
-   VolCatInfo = dcr->VolCatInfo;         /* structure assignment */
-   Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName);
-
-   if (VolCatInfo.VolCatName[0] == 0) {
-      Dmsg1(10,  "Could not open DVD device %s. No Volume name given.\n",
-         print_name());
-      Mmsg(errmsg, _("Could not open DVD device %s. No Volume name given.\n"),
-         print_name());
-      clear_opened();
-      return;
-   }
-
-   if (part == 0) {
-      Dmsg0(100, "Set part=1\n");
-      part = 1;                       /* count from 1 */
-      file_size = 0;
-   }
-   part_size = 0;
-   if (num_dvd_parts != VolCatInfo.VolCatParts) {
-      num_dvd_parts = VolCatInfo.VolCatParts;
-   }
-
-   /*
-    * If we are not trying to access the last part, set mode to 
-    *   OPEN_READ_ONLY as writing would be an error.
-    */
-   Dmsg2(100, "open DVD part=%d num_dvd_parts=%d\n", part, num_dvd_parts);
-   /* Now find the name of the part that we want to access */
-   if (part <= num_dvd_parts) {
-      omode = OPEN_READ_ONLY;
-      make_mounted_dvd_filename(this, archive_name);
-      set_part_spooled(false);
-   } else {
-      omode = OPEN_READ_WRITE;
-      make_spooled_dvd_filename(this, archive_name);
-      set_part_spooled(true);
-   }
-   set_mode(omode);
-
-   // Clear any previous blank_dvd status - we will recalculate it here
-   blank_dvd = false;
-
-   Dmsg3(99, "open_dvd_device: part=%d num_dvd_parts=%d, VolCatInfo.VolCatParts=%d\n",
-      part, num_dvd_parts, dcr->VolCatInfo.VolCatParts);
-     
-   if (mount(1)) {
-      Dmsg0(99, "DVD device mounted.\n");
-      if (num_dvd_parts == 0 && !truncating) {
-         /*
-          * If we can mount the device, and we are not truncating the DVD, 
-          * we usually want to abort. There is one exception, if there is 
-          * only one 0-sized file on the DVD, with the right volume name,
-          * we continue (it's the method used by truncate_dvd to truncate a volume).   
-          */
-         if (!check_can_write_on_non_blank_dvd(dcr)) {
-            Mmsg(errmsg, _("The DVD in device %s contains data, please blank it before writing.\n"), print_name());
-            Emsg0(M_FATAL, 0, errmsg);
-            unmount(1); /* Unmount the device, so the operator can change it. */
-            clear_opened();
-            return;
-         }
-         blank_dvd = true;
-      } else {
-         /*
-          * Ensure that we have the correct DVD loaded by looking for part1.
-          * We only succeed the open if it exists. Failure to do this could
-          * leave us trying to add a part to a different DVD!
-          */
-         uint32_t oldpart = part;
-         struct stat statp;
-         POOL_MEM part1_name(PM_FNAME);
-         part = 1;
-         make_mounted_dvd_filename(this, part1_name);
-         part = oldpart;
-         if (stat(part1_name.c_str(), &statp) < 0) {
-            berrno be;
-            Mmsg(errmsg, _("Unable to stat DVD part 1 file %s: ERR=%s\n"),
-               part1_name.c_str(), be.bstrerror());
-            Emsg0(M_FATAL, 0, errmsg);
-            clear_opened();
-            return;
-         }
-         if (!S_ISREG(statp.st_mode)) {
-            /* It is not a regular file */
-            Mmsg(errmsg, _("DVD part 1 is not a regular file %s.\n"),
-               part1_name.c_str());
-            Emsg0(M_FATAL, 0, errmsg);
-            clear_opened();
-            return;
-         }
-      }
-   } else {
-      Dmsg0(99, "DVD device mount failed.\n");
-      /* We cannot mount the device */
-      if (num_dvd_parts == 0) {
-         /* Run free space, check there is a media. */
-         if (!update_freespace()) {
-            Emsg0(M_FATAL, 0, errmsg);
-            clear_opened();
-            return;
-         }
-         if (have_media()) {
-            Dmsg1(100, "Could not mount device %s, this is not a problem (num_dvd_parts == 0), and have media.\n", print_name());
-         } else {
-            Mmsg(errmsg, _("There is no valid DVD in device %s.\n"), print_name());
-            Emsg0(M_FATAL, 0, errmsg);
-            clear_opened();
-            return;
-         }
-      }  else {
-         Mmsg(errmsg, _("Could not mount DVD device %s.\n"), print_name());
-         Emsg0(M_FATAL, 0, errmsg);
-         clear_opened();
-         return;
-      }
-   }
-   
-   Dmsg5(100, "open dev: DVD dev=%s mode=%s part=%d npart=%d volcatnparts=%d\n", 
-      archive_name.c_str(), mode_to_str(omode),
-      part, num_dvd_parts, dcr->VolCatInfo.VolCatParts);
-   openmode = omode;
-   Dmsg2(100, "openmode=%d %s\n", openmode, mode_to_str(openmode));
-   
-
-   /* If creating file, give 0640 permissions */
-   Dmsg3(100, "mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode), 
-         archive_name.c_str(), mode);
-   /* Use system open() */
-   if ((m_fd = ::open(archive_name.c_str(), mode, 0640)) < 0) {
-      berrno be;
-      Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), 
-            be.bstrerror());
-      // Should this be set if we try the create/open below
-      dev_errno = EIO; /* Interpreted as no device present by acquire.c:acquire_device_for_read(). */
-      Dmsg1(100, "open failed: %s", errmsg);
-      
-      /* Previous open failed. See if we can recover */
-      if ((omode == OPEN_READ_ONLY || omode == OPEN_READ_WRITE) &&
-          (part > num_dvd_parts)) {
-         /* If the last part (on spool), doesn't exist when accessing,
-          * create it. In read/write mode a write will be allowed (higher
-          * level software thinks that we are extending a pre-existing
-          * media. Reads for READ_ONLY will report immediately an EOF 
-          * Sometimes it is better to finish with an EOF than with an error. */
-         Dmsg1(100, "Creating last part on spool: %s\n", archive_name.c_str());
-         omode = CREATE_READ_WRITE;
-         set_mode(CREATE_READ_WRITE);
-         m_fd = ::open(archive_name.c_str(), mode, 0640);
-         set_mode(omode);
-      }
-   }
-   Dmsg1(100, "after open fd=%d\n", m_fd);
-   if (is_open()) {
-      if (omode == OPEN_READ_WRITE || omode == CREATE_READ_WRITE) {
-         set_append();
-      }
-      /* Get size of file */
-      if (fstat(m_fd, &filestat) < 0) {
-         berrno be;
-         dev_errno = errno;
-         Mmsg2(errmsg, _("Could not fstat: %s, ERR=%s\n"), archive_name.c_str(), 
-               be.bstrerror());
-         Dmsg1(100, "open failed: %s", errmsg);
-         /* Use system close() */
-         d_close(m_fd);
-         clear_opened();
-      } else {
-         part_size = filestat.st_size;
-         dev_errno = 0;
-         update_pos(dcr);                    /* update position */
-      }
-   }
-}
-
-
 /*
  * Rewind the device.
  *  Returns: true  on success
 /*
  * Rewind the device.
  *  Returns: true  on success
@@ -758,12 +588,6 @@ bool DEVICE::rewind(DCR *dcr)
    file_size = 0;
    file_addr = 0;
    if (m_fd < 0) {
    file_size = 0;
    file_addr = 0;
    if (m_fd < 0) {
-      if (!is_dvd()) { /* In case of major error, the fd is not open on DVD, so we don't want to abort. */
-         dev_errno = EBADF;
-         Mmsg1(errmsg, _("Bad call to rewind. Device %s not open\n"),
-            print_name());
-         Emsg0(M_ABORT, 0, errmsg);
-      }
       return false;
    }
    if (is_tape()) {
       return false;
    }
    if (is_tape()) {
@@ -815,7 +639,7 @@ bool DEVICE::rewind(DCR *dcr)
          }
          break;
       }
          }
          break;
       }
-   } else if (is_file() || is_dvd()) {
+   } else if (is_file()) {
       if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
          berrno be;
          dev_errno = errno;
       if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) {
          berrno be;
          dev_errno = errno;
@@ -876,7 +700,7 @@ bool DEVICE::eod(DCR *dcr)
    return fsf(VolCatInfo.VolCatFiles);
 #endif
 
    return fsf(VolCatInfo.VolCatFiles);
 #endif
 
-   Dmsg0(100, "eod\n");
+   Dmsg0(100, "Enter eod\n");
    if (at_eot()) {
       return true;
    }
    if (at_eot()) {
       return true;
    }
@@ -889,7 +713,7 @@ bool DEVICE::eod(DCR *dcr)
    }
    if (!is_tape()) {
       pos = lseek(dcr, (boffset_t)0, SEEK_END);
    }
    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();
       if (pos >= 0) {
          update_pos(dcr);
          set_eot();
@@ -899,6 +723,7 @@ bool DEVICE::eod(DCR *dcr)
       berrno be;
       Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
              print_name(), be.bstrerror());
       berrno be;
       Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
              print_name(), be.bstrerror());
+      Dmsg0(100, errmsg);
       return false;
    }
 #ifdef MTEOM
       return false;
    }
 #ifdef MTEOM
@@ -907,6 +732,7 @@ bool DEVICE::eod(DCR *dcr)
       /* If unknown position, rewind */
       if (get_os_tape_file() < 0) {
         if (!rewind(NULL)) {
       /* If unknown position, rewind */
       if (get_os_tape_file() < 0) {
         if (!rewind(NULL)) {
+          Dmsg0(100, "Rewind error\n");
           return false;
         }
       }
           return false;
         }
       }
@@ -935,6 +761,7 @@ bool DEVICE::eod(DCR *dcr)
          update_pos(dcr);
          Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"),
             print_name(), be.bstrerror());
          update_pos(dcr);
          Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"),
             print_name(), be.bstrerror());
+         Dmsg0(100, errmsg);
          return false;
       }
 
          return false;
       }
 
@@ -944,6 +771,7 @@ bool DEVICE::eod(DCR *dcr)
          clrerror(-1);
          Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"),
             print_name(), be.bstrerror());
          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);
          return false;
       }
       Dmsg1(100, "EOD file=%d\n", os_file);
@@ -957,6 +785,7 @@ bool DEVICE::eod(DCR *dcr)
        * Rewind then use FSF until EOT reached
        */
       if (!rewind(NULL)) {
        * Rewind then use FSF until EOT reached
        */
       if (!rewind(NULL)) {
+         Dmsg0(100, "Rewind error.\n");
          return false;
       }
       /*
          return false;
       }
       /*
@@ -966,7 +795,7 @@ bool DEVICE::eod(DCR *dcr)
       for (file_num=file; !at_eot(); file_num++) {
          Dmsg0(200, "eod: doing fsf 1\n");
          if (!fsf(1)) {
       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;
          }
          /*
             return false;
          }
          /*
@@ -1025,8 +854,7 @@ bool DEVICE::update_pos(DCR *dcr)
       return false;
    }
 
       return false;
    }
 
-   /* Find out where we are */
-   if (is_file() || is_dvd()) {
+   if (is_file()) {
       file = 0;
       file_addr = 0;
       pos = lseek(dcr, (boffset_t)0, SEEK_CUR);
       file = 0;
       file_addr = 0;
       pos = lseek(dcr, (boffset_t)0, SEEK_CUR);
@@ -1035,7 +863,7 @@ bool DEVICE::update_pos(DCR *dcr)
          dev_errno = errno;
          Pmsg1(000, _("Seek error: ERR=%s\n"), be.bstrerror());
          Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
          dev_errno = errno;
          Pmsg1(000, _("Seek error: ERR=%s\n"), be.bstrerror());
          Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"),
-            print_name(), be.bstrerror());
+               print_name(), be.bstrerror());
          ok = false;
       } else {
          file_addr = pos;
          ok = false;
       } else {
          file_addr = pos;
@@ -1858,7 +1686,7 @@ void DEVICE::clrerror(int func)
 }
 #endif
 
 }
 #endif
 
-/* Clear Subsystem Exception OSF1 */
+/* Clear Subsystem Exception TRU64 */
 #ifdef MTCSE
 {
    struct mtop mt_com;
 #ifdef MTCSE
 {
    struct mtop mt_com;
@@ -1891,6 +1719,7 @@ void DEVICE::clear_volhdr()
 {
    Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName);
    memset(&VolHdr, 0, sizeof(VolHdr));
 {
    Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName);
    memset(&VolHdr, 0, sizeof(VolHdr));
+   setVolCatInfo(false);
 }
 
 
 }
 
 
@@ -1900,9 +1729,7 @@ void DEVICE::clear_volhdr()
 void DEVICE::close()
 {
    Dmsg1(100, "close_dev %s\n", print_name());
 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(),
 
    if (!is_open()) {
       Dmsg2(100, "device %s already closed vol=%s\n", print_name(),
@@ -1917,8 +1744,11 @@ void DEVICE::close()
       unlock_door(); 
    default:
       d_close(m_fd);
       unlock_door(); 
    default:
       d_close(m_fd);
+      break;
    }
 
    }
 
+   unmount(1);                        /* do unmount if required */
+
    /* Clean up device packet so it can be reused */
    clear_opened();
    /*
    /* Clean up device packet so it can be reused */
    clear_opened();
    /*
@@ -1966,8 +1796,6 @@ void DEVICE::close_part(DCR * /*dcr*/)
 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
 {
    switch (dev_type) {
 boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence)
 {
    switch (dev_type) {
-   case B_DVD_DEV:
-      return lseek_dvd(dcr, offset, whence);
    case B_FILE_DEV:
 #if defined(HAVE_WIN32)
       return ::_lseeki64(m_fd, (__int64)offset, whence);
    case B_FILE_DEV:
 #if defined(HAVE_WIN32)
       return ::_lseeki64(m_fd, (__int64)offset, whence);
@@ -1990,8 +1818,6 @@ bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
    case B_TAPE_DEV:
       /* maybe we should rewind and write and eof ???? */
       return true;                    /* we don't really truncate tapes */
    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:
       if (ftruncate(m_fd, 0) != 0) {
          berrno be;
    case B_FILE_DEV:
       if (ftruncate(m_fd, 0) != 0) {
          berrno be;
@@ -2054,74 +1880,164 @@ bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
    return false;
 }
 
    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 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;
    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:
+      if (requires_mount() && device->mount_command) {
+         return do_file_mount(1, timeout);
+      }
+      break;
+   default:
+      break;
+   }
+
    return true;
 }
 
    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 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;
 }
 
    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;
 {
    POOL_MEM ocmd(PM_FNAME);
    POOLMEM *results;
    char *icmd;
-   int status, timeout;
-   
-   Dsm_check(1);
+   int status, tries;
+   berrno be;
+
+   Dsm_check(200);
    if (mount) {
    if (mount) {
-      if (is_mounted()) {
-         Dmsg0(200, "======= mount=1\n");
-         return true;
-      }
       icmd = device->mount_command;
    } else {
       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(200);
+      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(200);
+   if (mount) {
+      icmd = device->mount_command;
+   } else {
       icmd = device->unmount_command;
    }
    
    clear_freespace_ok();
    edit_mount_codes(ocmd, icmd);
    
       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 10 times to (un)mount the device. This should perhaps be configurable. */
 
    if (dotimeout) {
       /* Try at most 10 times to (un)mount the device. This should perhaps be configurable. */
-      timeout = 10;
+      tries = 10;
    } else {
    } else {
-      timeout = 0;
+      tries = 1;
    }
    results = get_memory(4000);
 
    /* If busy retry each second */
    }
    results = get_memory(4000);
 
    /* 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;
       /* Doesn't work with internationalization (This is not a problem) */
       if (mount && fnmatch("*is already mounted on*", results, 0) == 0) {
          break;
@@ -2129,37 +2045,24 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       if (!mount && fnmatch("* not mounted*", results, 0) == 0) {
          break;
       }
       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());
          /* 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;
       }
          }
          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;
       name_max = pathconf(".", _PC_NAME_MAX);
       if (name_max < 1024) {
          name_max = 1024;
@@ -2168,7 +2071,7 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       if (!(dp = opendir(device->mount_point))) {
          berrno be;
          dev_errno = errno;
       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;
       }
                device->mount_point, print_name(), be.bstrerror());
          goto get_out;
       }
@@ -2178,7 +2081,7 @@ bool DEVICE::do_mount(int mount, int dotimeout)
       while (1) {
          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
             dev_errno = EIO;
       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;
          }
                   device->mount_point, print_name());
             break;
          }
@@ -2186,13 +2089,13 @@ bool DEVICE::do_mount(int mount, int dotimeout)
             count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */
             break;
          } else {
             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);
       
          }
       }
       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 */
       
       if (count > 0) {
          /* If we got more than ., .. and .keep */
@@ -2212,7 +2115,7 @@ get_out:
       set_mounted(false);
       free_pool_memory(results);
       Dmsg0(200, "============ mount=0\n");
       set_mounted(false);
       free_pool_memory(results);
       Dmsg0(200, "============ mount=0\n");
-      Dsm_check(1);
+      Dsm_check(200);
       return false;
    }
    
       return false;
    }
    
@@ -2372,7 +2275,7 @@ int32_t DEVICE::get_os_tape_file()
 char *
 dev_vol_name(DEVICE *dev)
 {
 char *
 dev_vol_name(DEVICE *dev)
 {
-   return dev->VolCatInfo.VolCatName;
+   return dev->getVolCatName();
 }
 
 
 }
 
 
@@ -2407,7 +2310,7 @@ void DEVICE::term(void)
    if (device) {
       device->dev = NULL;
    }
    if (device) {
       device->dev = NULL;
    }
-   free((char *)this);
+   delete this;
 }
 
 /*
 }
 
 /*