]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/dev.c
kes Apply patch supplied in bug #656 to pass priority field
[bacula/bacula] / bacula / src / stored / dev.c
index 820bd43dd10d78759225c60414b3d6e3240046f3..b52f92353b737bf546032657e18082c4e7ece6e8 100644 (file)
@@ -12,7 +12,7 @@
  *        to carry out operations without worrying about who
  *        set what lock (i.e. race conditions).
  *
- *     Note, this is the device dependent code, and my have
+ *     Note, this is the device dependent code, and may have
  *           to be modified for each system, but is meant to
  *           be as "generic" as possible.
  *
@@ -103,7 +103,7 @@ init_dev(JCR *jcr, DEVRES *device)
    DCR *dcr = NULL;
    DEVICE *dev;
 
-   
+
    /* If no device type specified, try to guess */
    if (!device->dev_type) {
       /* Check that device is available */
@@ -131,7 +131,7 @@ init_dev(JCR *jcr, DEVRES *device)
 
    dev = (DEVICE *)malloc(sizeof(DEVICE));
    memset(dev, 0, sizeof(DEVICE));
-   dev->state = ST_MALLOC;
+   dev->Slot = -1;       /* unknown */
 
    /* Copy user supplied device parameters from Resource */
    dev->dev_name = get_memory(strlen(device->device_name)+1);
@@ -139,6 +139,7 @@ init_dev(JCR *jcr, DEVRES *device)
    dev->prt_name = get_memory(strlen(device->device_name) + strlen(device->hdr.name) + 20);
    /* We edit "Resource-name" (physical-name) */
    Mmsg(dev->prt_name, "\"%s\" (%s)", device->hdr.name, device->device_name);
+   Dmsg1(400, "Allocate dev=%s\n", dev->print_name());
    dev->capabilities = device->cap_bits;
    dev->min_block_size = device->min_block_size;
    dev->max_block_size = device->max_block_size;
@@ -162,7 +163,9 @@ init_dev(JCR *jcr, DEVRES *device)
    if (dev->vol_poll_interval && dev->vol_poll_interval < 60) {
       dev->vol_poll_interval = 60;
    }
+   /* Link the dev and device structures together */
    dev->device = device;
+   device->dev = dev;
 
    if (dev->is_fifo()) {
       dev->capabilities |= CAP_STREAM; /* set stream device */
@@ -262,10 +265,14 @@ DEVICE::open(DCR *dcr, int omode)
       if (openmode == omode) {
          return fd;
       } else {
-        ::close(fd); /* use system close so correct mode will be used on open */
-        clear_opened();
-        Dmsg0(100, "Close fd for mode change.\n");
-        preserve = state & (ST_LABEL|ST_APPEND|ST_READ);
+         if (is_tape()) {
+            tape_close(fd);
+         } else {
+            ::close(fd);
+         }
+         clear_opened();
+         Dmsg0(100, "Close fd for mode change.\n");
+         preserve = state & (ST_LABEL|ST_APPEND|ST_READ);
       }
    }
    if (dcr) {
@@ -275,6 +282,7 @@ DEVICE::open(DCR *dcr, int omode)
    Dmsg4(29, "open dev: type=%d dev_name=%s vol=%s mode=%s\n", dev_type,
          print_name(), VolCatInfo.VolCatName, 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);
@@ -286,9 +294,7 @@ DEVICE::open(DCR *dcr, int omode)
       open_file_device(dcr, omode);
    }
    state |= preserve;                 /* reset any important state info */
-   if (preserve) {
-      Dmsg1(000, "preserve=0x%x\n", preserve);
-   }
+   Dmsg2(100, "preserve=0x%x fd=%d\n", preserve, fd);
    return fd;
 }
 
@@ -321,9 +327,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    int nonblocking = O_NONBLOCK;
    Dmsg0(29, "open dev: device is tape\n");
 
-   if (is_autochanger()) {
-      get_autochanger_loaded_slot(dcr);
-   }
+   get_autochanger_loaded_slot(dcr);
 
    set_mode(omode);
    timeout = max_open_wait;
@@ -336,6 +340,17 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
    Dmsg3(100, "Try open %s mode=%s nonblocking=%d\n", print_name(),
       mode_to_str(omode), nonblocking);
    /* Use system open() */
+
+#if defined(HAVE_WIN32)
+   if ((fd = tape_open(dev_name, mode)) < 0) {
+      berrno be;
+      dev_errno = errno;
+
+      Mmsg2(errmsg, _("Unable to open device %s: ERR=%s\n"),
+            print_name(), be.strerror(dev_errno));
+      Jmsg0(dcr->jcr, M_FATAL, 0, errmsg);
+   }
+#else
    while ((fd = ::open(dev_name, mode+nonblocking)) < 0) {
       berrno be;
       dev_errno = errno;
@@ -361,6 +376,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
       Jmsg0(dcr->jcr, M_FATAL, 0, errmsg);
       break;
    }
+#endif
 
    if (fd >= 0) {
       openmode = omode;              /* save open mode */
@@ -407,9 +423,7 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
 {
    POOL_MEM archive_name(PM_FNAME);
 
-   if (is_autochanger()) {
-      get_autochanger_loaded_slot(dcr);
-   }
+   get_autochanger_loaded_slot(dcr);
 
    /*
     * Handle opening of File Archive (not a tape)
@@ -418,7 +432,7 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
    pm_strcpy(archive_name, dev_name);
    /*  
     * If this is a virtual autochanger (i.e. changer_res != NULL)
-    *  we simply use the deviced name, assuming it has been
+    *  we simply use the device name, assuming it has been
     *  appropriately setup by the "autochanger".
     */
    if (!device->changer_res) {
@@ -455,7 +469,7 @@ void DEVICE::open_file_device(DCR *dcr, int omode)
       update_pos_dev(this);                /* update position */
    }
    Dmsg4(29, "open dev: disk fd=%d opened, part=%d/%d, part_size=%u\n", 
-      fd, part, num_parts, part_size);
+      fd, part, num_dvd_parts, part_size);
 }
 
 /*
@@ -475,6 +489,17 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
    Dmsg3(29, "Enter: open_dvd_dev: %s dev=%s mode=%s\n", is_dvd()?"DVD":"disk",
          archive_name.c_str(), mode_to_str(omode));
 
+   /*
+    * For a DVD we must alway 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.
+    */
+   memcpy(&VolCatInfo, &dcr->VolCatInfo, sizeof(VolCatInfo));
+   Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName);
+
    if (VolCatInfo.VolCatName[0] == 0) {
       Dmsg1(10,  "Could not open file device %s. No Volume name given.\n",
          print_name());
@@ -485,23 +510,29 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
    }
 
    if (part == 0) {
+      Dmsg0(100, "Set part=1\n");
+      part = 1;                       /* count from 1 */
       file_size = 0;
    }
    part_size = 0;
-
-   Dmsg2(99, "open_dvd_device: num_parts=%d, VolCatInfo.VolCatParts=%d\n",
-      dcr->dev->num_parts, dcr->VolCatInfo.VolCatParts);
-   if (dcr->dev->num_parts < dcr->VolCatInfo.VolCatParts) {
-      Dmsg2(99, "open_dvd_device: num_parts updated to %d (was %d)\n",
-         dcr->VolCatInfo.VolCatParts, dcr->dev->num_parts);
-      dcr->dev->num_parts = dcr->VolCatInfo.VolCatParts;
+   if (num_dvd_parts != VolCatInfo.VolCatParts) {
+      num_dvd_parts = VolCatInfo.VolCatParts;
    }
+   // Clear any previous truncated_dvd status - we will recalculate it here
+   truncated_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_dvd(this, 1)) {
-      if ((num_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). */
+      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 media in the device %s is not empty, please blank it before writing anything to it.\n"), print_name());
             Emsg0(M_FATAL, 0, errmsg);
@@ -509,23 +540,27 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
             clear_opened();
             return;
          }
+         truncated_dvd = true;
       }
    } else {
+      Dmsg0(99, "DVD device mount failed.\n");
       /* We cannot mount the device */
-      if (num_parts == 0) {
+      if (num_dvd_parts == 0) {
          /* Run free space, check there is a media. */
-         update_free_space_dev(this);
-         if (have_media()) {
-            Dmsg1(29, "Could not mount device %s, this is not a problem (num_parts == 0), and have media.\n", print_name());
+         if (!update_free_space_dev(this)) {
+            Emsg0(M_FATAL, 0, errmsg);
+            clear_opened();
+            return;
          }
-         else {
+         if (have_media()) {
+            Dmsg1(29, "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 media in the device %s.\n"), print_name());
             Emsg0(M_FATAL, 0, errmsg);
             clear_opened();
             return;
          }
-      }
-      else {
+      }  else {
          Mmsg(errmsg, _("Could not mount device %s.\n"), print_name());
          Emsg0(M_FATAL, 0, errmsg);
          clear_opened();
@@ -533,9 +568,9 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
       }
    }
    
-   Dmsg6(29, "open dev: %s dev=%s mode=%s part=%d npart=%d volcatnparts=%d\n", 
-      is_dvd()?"DVD":"disk", archive_name.c_str(), mode_to_str(omode),
-      part, num_parts, dcr->VolCatInfo.VolCatParts);
+   Dmsg5(29, "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));
    
@@ -543,11 +578,12 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
     * If we are not trying to access the last part, set mode to 
     *   OPEN_READ_ONLY as writing would be an error.
     */
-   if (part < num_parts) {
+   Dmsg2(29, "open DVD part=%d num_dvd_parts=%d\n", part, num_dvd_parts);
+   if (part <= num_dvd_parts) {
       omode = OPEN_READ_ONLY;
       make_mounted_dvd_filename(this, archive_name);
-   }
-   else {
+   } else {
+      omode = OPEN_READ_WRITE;
       make_spooled_dvd_filename(this, archive_name);
    }
    set_mode(omode);
@@ -560,26 +596,30 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
       berrno be;
       Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), 
             be.strerror());
+      // 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(29, "open failed: %s", errmsg);
       
-      if ((omode == OPEN_READ_ONLY) && (part == num_parts)) {
-         /* If the last part (on spool), doesn't exists when reading, create it and read from it
-          * (it will report immediately an EOF):
+      /* 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. */
-         set_mode(OPEN_READ_WRITE);
+         Dmsg1(29, "Creating last part on spool: %s\n", archive_name.c_str());
+         omode = CREATE_READ_WRITE;
+         set_mode(CREATE_READ_WRITE);
          fd = ::open(archive_name.c_str(), mode, 0640);
-         set_mode(OPEN_READ_ONLY);
+         set_mode(omode);
       }
-      
-      /* We don't need it. Only the last part is on spool */
-      /*if (omode == OPEN_READ_ONLY) {
-         make_spooled_dvd_filename(this, archive_name);
-         fd = ::open(archive_name.c_str(), mode, 0640);  // try on spool
-      }*/
    }
    Dmsg1(100, "after open fd=%d\n", fd);
    if (fd >= 0) {
+      if (omode == OPEN_READ_WRITE || omode == CREATE_READ_WRITE) {
+         set_append();
+      }
       /* Get size of file */
       if (fstat(fd, &filestat) < 0) {
          berrno be;
@@ -594,19 +634,6 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
          part_size = filestat.st_size;
          dev_errno = 0;
          update_pos_dev(this);                /* update position */
-         
-         /* NB: It seems this code is wrong... part number is incremented in open_next_part, not here */
-         
-         /* Check if just created Volume  part */
-/*         if (omode == OPEN_READ_WRITE && (part == 0 || part_size == 0)) {
-            part++;
-            num_parts = part;
-            VolCatInfo.VolCatParts = num_parts;
-         } else {
-            if (part == 0) {             // we must have opened the first part
-               part++;
-            }
-         }*/
       }
    }
 }
@@ -624,6 +651,10 @@ bool DEVICE::rewind(DCR *dcr)
    bool first = true;
 
    Dmsg3(400, "rewind res=%d fd=%d %s\n", reserved_device, fd, print_name());
+   state &= ~(ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
+   block_num = file = 0;
+   file_size = 0;
+   file_addr = 0;
    if (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;
@@ -633,10 +664,6 @@ bool DEVICE::rewind(DCR *dcr)
       }
       return false;
    }
-   state &= ~(ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
-   block_num = file = 0;
-   file_size = 0;
-   file_addr = 0;
    if (is_tape()) {
       mt_com.mt_op = MTREW;
       mt_com.mt_count = 1;
@@ -645,9 +672,9 @@ bool DEVICE::rewind(DCR *dcr)
        * retrying every 5 seconds.
        */
       for (i=max_rewind_wait; ; i -= 5) {
-         if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
             berrno be;
-            clrerror_dev(this, MTREW);
+            clrerror(MTREW);
             if (i == max_rewind_wait) {
                Dmsg1(200, "Rewind error, %s. retrying ...\n", be.strerror());
             }
@@ -659,7 +686,7 @@ bool DEVICE::rewind(DCR *dcr)
              */
             if (first && dcr) {
                int open_mode = openmode;
-               ::close(fd);
+               tape_close(fd);
                clear_opened();
                open(dcr, open_mode);
                if (fd < 0) {
@@ -668,18 +695,25 @@ bool DEVICE::rewind(DCR *dcr)
                first = false;
                continue;
             }
+#ifdef HAVE_SUN_OS
+            if (dev_errno == EIO) {         
+               Mmsg1(errmsg, _("No tape loaded or drive offline on %s.\n"), print_name());
+               return false;
+            }
+#else
             if (dev_errno == EIO && i > 0) {
                Dmsg0(200, "Sleeping 5 seconds.\n");
                bmicrosleep(5, 0);
                continue;
             }
+#endif
             Mmsg2(errmsg, _("Rewind error on %s. ERR=%s.\n"),
                print_name(), be.strerror());
             return false;
          }
          break;
       }
-   } else if (is_file()) {      
+   } else if (is_file() || is_dvd()) {
       if (lseek_dev(this, (off_t)0, SEEK_SET) < 0) {
          berrno be;
          dev_errno = errno;
@@ -779,7 +813,7 @@ bool DEVICE::eod()
    if (at_eot()) {
       return true;
    }
-   state &= ~(ST_EOF);  /* remove EOF flags */
+   clear_eof();         /* remove EOF flag */
    block_num = file = 0;
    file_size = 0;
    file_addr = 0;
@@ -791,7 +825,7 @@ bool DEVICE::eod()
 //    Dmsg1(100, "====== Seek to %lld\n", pos);
       if (pos >= 0) {
          update_pos_dev(this);
-         state |= ST_EOT;
+         set_eot();
          return true;
       }
       dev_errno = errno;
@@ -827,9 +861,9 @@ bool DEVICE::eod()
          mt_com.mt_count = 1;
       }
 
-      if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
+      if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
          berrno be;
-         clrerror_dev(this, mt_com.mt_op);
+         clrerror(mt_com.mt_op);
          Dmsg1(50, "ioctl error: %s\n", be.strerror());
          update_pos_dev(this);
          Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"),
@@ -839,7 +873,7 @@ bool DEVICE::eod()
 
       if (!dev_get_os_pos(this, &mt_stat)) {
          berrno be;
-         clrerror_dev(this, -1);
+         clrerror(-1);
          Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"),
             print_name(), be.strerror());
          return false;
@@ -937,6 +971,8 @@ bool update_pos_dev(DEVICE *dev)
          ok = false;
       } else {
          dev->file_addr = pos;
+         dev->block_num = (uint32_t)pos;
+         dev->file = (uint32_t)(pos >> 32);
       }
    }
    return ok;
@@ -969,7 +1005,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 (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) {
+      if (tape_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"),
@@ -1015,6 +1051,40 @@ uint32_t status_dev(DEVICE *dev)
          stat |= BMT_IM_REP_EN;
          Pmsg0(-20, " IM_REP_EN");
       }
+#elif defined(HAVE_WIN32)
+      if (GMT_EOF(mt_stat.mt_gstat)) {
+         stat |= BMT_EOF;
+         Pmsg0(-20, " EOF");
+      }
+      if (GMT_BOT(mt_stat.mt_gstat)) {
+         stat |= BMT_BOT;
+         Pmsg0(-20, " BOT");
+      }
+      if (GMT_EOT(mt_stat.mt_gstat)) {
+         stat |= BMT_EOT;
+         Pmsg0(-20, " EOT");
+      }
+      if (GMT_EOD(mt_stat.mt_gstat)) {
+         stat |= BMT_EOD;
+         Pmsg0(-20, " EOD");
+      }
+      if (GMT_WR_PROT(mt_stat.mt_gstat)) {
+         stat |= BMT_WR_PROT;
+         Pmsg0(-20, " WR_PROT");
+      }
+      if (GMT_ONLINE(mt_stat.mt_gstat)) {
+         stat |= BMT_ONLINE;
+         Pmsg0(-20, " ONLINE");
+      }
+      if (GMT_DR_OPEN(mt_stat.mt_gstat)) {
+         stat |= BMT_DR_OPEN;
+         Pmsg0(-20, " DR_OPEN");
+      }
+      if (GMT_IM_REP_EN(mt_stat.mt_gstat)) {
+         stat |= BMT_IM_REP_EN;
+         Pmsg0(-20, " IM_REP_EN");
+      }
+
 #endif /* !SunOS && !OSF */
       if (dev->has_cap(CAP_MTIOCGET)) {
          Pmsg2(-20, _(" file=%d block=%d\n"), mt_stat.mt_fileno, mt_stat.mt_blkno);
@@ -1062,7 +1132,7 @@ bool load_dev(DEVICE *dev)
    dev->file_addr = 0;
    mt_com.mt_op = MTLOAD;
    mt_com.mt_count = 1;
-   if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+   if (tape_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"),
@@ -1090,15 +1160,14 @@ bool DEVICE::offline()
    block_num = file = 0;
    file_size = 0;
    file_addr = 0;
-   part = 0;
 #ifdef MTUNLOCK
    mt_com.mt_op = MTUNLOCK;
    mt_com.mt_count = 1;
-   ioctl(fd, MTIOCTOP, (char *)&mt_com);
+   tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
 #endif
    mt_com.mt_op = MTOFFL;
    mt_com.mt_count = 1;
-   if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
+   if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) {
       berrno be;
       dev_errno = errno;
       Mmsg2(errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"),
@@ -1124,7 +1193,7 @@ bool DEVICE::offline_or_rewind()
     *  such as backspacing after writing and EOF. If it is not
     *  done, all future references to the drive get and I/O error.
     */
-      clrerror_dev(this, MTREW);
+      clrerror(MTREW);
       return rewind(NULL);
    }
 }
@@ -1171,12 +1240,12 @@ bool DEVICE::fsf(int num)
    if (has_cap(CAP_FSF) && has_cap(CAP_MTIOCGET) && has_cap(CAP_FASTFSF)) {
       mt_com.mt_op = MTFSF;
       mt_com.mt_count = num;
-      stat = ioctl(fd, MTIOCTOP, (char *)&mt_com);
+      stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
       if (stat < 0 || !dev_get_os_pos(this, &mt_stat)) {
          berrno be;
          set_eot();
          Dmsg0(200, "Set ST_EOT\n");
-         clrerror_dev(this, MTFSF);
+         clrerror(MTFSF);
          Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"),
             print_name(), be.strerror());
          Dmsg1(200, "%s", errmsg);
@@ -1208,7 +1277,7 @@ bool DEVICE::fsf(int num)
       mt_com.mt_count = 1;
       while (num-- && !at_eot()) {
          Dmsg0(100, "Doing read before fsf\n");
-         if ((stat = read(fd, (char *)rbuf, rbuf_len)) < 0) {
+         if ((stat = tape_read(fd, (char *)rbuf, rbuf_len)) < 0) {
             if (errno == ENOMEM) {     /* tape record exceeds buf len */
                stat = rbuf_len;        /* This is OK */
             /*
@@ -1220,7 +1289,7 @@ bool DEVICE::fsf(int num)
             } else {
                berrno be;
                set_eot();
-               clrerror_dev(this, -1);
+               clrerror(-1);
                Dmsg2(100, "Set ST_EOT read errno=%d. ERR=%s\n", dev_errno,
                   be.strerror());
                Mmsg2(errmsg, _("read error on %s. ERR=%s.\n"),
@@ -1247,12 +1316,12 @@ bool DEVICE::fsf(int num)
          }
 
          Dmsg0(100, "Doing MTFSF\n");
-         stat = ioctl(fd, MTIOCTOP, (char *)&mt_com);
+         stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
          if (stat < 0) {                 /* error => EOT */
             berrno be;
             set_eot();
             Dmsg0(100, "Set ST_EOT\n");
-            clrerror_dev(this, MTFSF);
+            clrerror(MTFSF);
             Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"),
                print_name(), be.strerror());
             Dmsg0(100, "Got < 0 for MTFSF\n");
@@ -1281,10 +1350,12 @@ bool DEVICE::fsf(int num)
    }
    update_pos_dev(this);
    Dmsg1(200, "Return %d from FSF\n", stat);
-   if (at_eof())
+   if (at_eof()) {
       Dmsg0(200, "ST_EOF set on exit FSF\n");
-   if (at_eot())
+   }
+   if (at_eot()) {
       Dmsg0(200, "ST_EOT set on exit FSF\n");
+   }
    Dmsg1(200, "Return from FSF file=%d\n", file);
    return stat == 0;
 }
@@ -1312,16 +1383,17 @@ bool DEVICE::bsf(int num)
       return false;
    }
    Dmsg0(29, "bsf\n");
-   state &= ~(ST_EOT|ST_EOF);
+   clear_eot();
+   clear_eof();
    file -= num;
    file_addr = 0;
    file_size = 0;
    mt_com.mt_op = MTBSF;
    mt_com.mt_count = num;
-   stat = ioctl(fd, MTIOCTOP, (char *)&mt_com);
+   stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    if (stat < 0) {
       berrno be;
-      clrerror_dev(this, MTBSF);
+      clrerror(MTBSF);
       Mmsg2(errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"),
          print_name(), be.strerror());
    }
@@ -1358,14 +1430,14 @@ bool DEVICE::fsr(int num)
    Dmsg1(29, "fsr %d\n", num);
    mt_com.mt_op = MTFSR;
    mt_com.mt_count = num;
-   stat = ioctl(fd, MTIOCTOP, (char *)&mt_com);
+   stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    if (stat == 0) {
       clear_eof();
       block_num += num;
    } else {
       berrno be;
       struct mtget mt_stat;
-      clrerror_dev(this, MTFSR);
+      clrerror(MTFSR);
       Dmsg1(100, "FSF fail: ERR=%s\n", be.strerror());
       if (dev_get_os_pos(this, &mt_stat)) {
          Dmsg4(100, "Adjust from %d:%d to %d:%d\n", file,
@@ -1391,41 +1463,41 @@ bool DEVICE::fsr(int num)
  *   Returns:  false on failure
  *             true  on success
  */
-bool
-bsr_dev(DEVICE *dev, int num)
+bool DEVICE::bsr(int num)
 {
    struct mtop mt_com;
    int stat;
 
-   if (dev->fd < 0) {
-      dev->dev_errno = EBADF;
-      Mmsg0(dev->errmsg, _("Bad call to bsr_dev. Device not open\n"));
-      Emsg0(M_FATAL, 0, dev->errmsg);
+   if (fd < 0) {
+      dev_errno = EBADF;
+      Mmsg0(errmsg, _("Bad call to bsr_dev. Device not open\n"));
+      Emsg0(M_FATAL, 0, errmsg);
       return false;
    }
 
-   if (!dev->is_tape()) {
+   if (!is_tape()) {
       return false;
    }
 
-   if (!dev->has_cap(CAP_BSR)) {
-      Mmsg1(dev->errmsg, _("ioctl MTBSR not permitted on %s.\n"), dev->print_name());
+   if (!has_cap(CAP_BSR)) {
+      Mmsg1(errmsg, _("ioctl MTBSR not permitted on %s.\n"), print_name());
       return false;
    }
 
    Dmsg0(29, "bsr_dev\n");
-   dev->block_num -= num;
-   dev->state &= ~(ST_EOF|ST_EOT|ST_EOF);
+   block_num -= num;
+   clear_eof();
+   clear_eot();
    mt_com.mt_op = MTBSR;
    mt_com.mt_count = num;
-   stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
+   stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    if (stat < 0) {
       berrno be;
-      clrerror_dev(dev, MTBSR);
-      Mmsg2(dev->errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"),
-         dev->print_name(), be.strerror());
+      clrerror(MTBSR);
+      Mmsg2(errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"),
+         print_name(), be.strerror());
    }
-   update_pos_dev(dev);
+   update_pos_dev(this);
    return stat == 0;
 }
 
@@ -1434,59 +1506,58 @@ bsr_dev(DEVICE *dev, int num)
  * Returns: false on failure
  *          true  on success
  */
-bool
-reposition_dev(DEVICE *dev, uint32_t file, uint32_t block)
+bool DEVICE::reposition(uint32_t rfile, uint32_t rblock)
 {
-   if (dev->fd < 0) {
-      dev->dev_errno = EBADF;
-      Mmsg0(dev->errmsg, _("Bad call to reposition_dev. Device not open\n"));
-      Emsg0(M_FATAL, 0, dev->errmsg);
+   if (fd < 0) {
+      dev_errno = EBADF;
+      Mmsg0(errmsg, _("Bad call to reposition. Device not open\n"));
+      Emsg0(M_FATAL, 0, errmsg);
       return false;
    }
 
-   if (!dev->is_tape()) {
-      off_t pos = (((off_t)file)<<32) + (off_t)block;
+   if (!is_tape()) {
+      off_t pos = (((off_t)rfile)<<32) + (off_t)rblock;
       Dmsg1(100, "===== lseek_dev to %d\n", (int)pos);
-      if (lseek_dev(dev, pos, SEEK_SET) == (off_t)-1) {
+      if (lseek_dev(this, pos, SEEK_SET) == (off_t)-1) {
          berrno be;
-         dev->dev_errno = errno;
-         Mmsg2(dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"),
-            dev->print_name(), be.strerror());
+         dev_errno = errno;
+         Mmsg2(errmsg, _("lseek_dev error on %s. ERR=%s.\n"),
+            print_name(), be.strerror());
          return false;
       }
-      dev->file = file;
-      dev->block_num = block;
-      dev->file_addr = pos;
+      file = rfile;
+      block_num = rblock;
+      file_addr = pos;
       return true;
    }
-   Dmsg4(100, "reposition_dev from %u:%u to %u:%u\n",
-      dev->file, dev->block_num, file, block);
-   if (file < dev->file) {
+   Dmsg4(100, "reposition from %u:%u to %u:%u\n",
+      file, block_num, rfile, rblock);
+   if (rfile < file) {
       Dmsg0(100, "Rewind\n");
-      if (!dev->rewind(NULL)) {
+      if (!rewind(NULL)) {
          return false;
       }
    }
-   if (file > dev->file) {
-      Dmsg1(100, "fsf %d\n", file-dev->file);
-      if (!dev->fsf(file-dev->file)) {
-         Dmsg1(100, "fsf failed! ERR=%s\n", strerror_dev(dev));
+   if (rfile > file) {
+      Dmsg1(100, "fsf %d\n", rfile-file);
+      if (!fsf(rfile-file)) {
+         Dmsg1(100, "fsf failed! ERR=%s\n", bstrerror());
          return false;
       }
-      Dmsg2(100, "wanted_file=%d at_file=%d\n", file, dev->file);
+      Dmsg2(100, "wanted_file=%d at_file=%d\n", rfile, file);
    }
-   if (block < dev->block_num) {
-      Dmsg2(100, "wanted_blk=%d at_blk=%d\n", block, dev->block_num);
+   if (rblock < block_num) {
+      Dmsg2(100, "wanted_blk=%d at_blk=%d\n", rblock, block_num);
       Dmsg0(100, "bsf 1\n");
-      dev->bsf(1);
+      bsf(1);
       Dmsg0(100, "fsf_dev 1\n");
-      dev->fsf(1);
-      Dmsg2(100, "wanted_blk=%d at_blk=%d\n", block, dev->block_num);
+      fsf(1);
+      Dmsg2(100, "wanted_blk=%d at_blk=%d\n", rblock, block_num);
    }
-   if (dev->has_cap(CAP_POSITIONBLOCKS) && block > dev->block_num) {
+   if (has_cap(CAP_POSITIONBLOCKS) && rblock > block_num) {
       /* Ignore errors as Bacula can read to the correct block */
-      Dmsg1(100, "fsr %d\n", block-dev->block_num);
-      return dev->fsr(block-dev->block_num);
+      Dmsg1(100, "fsr %d\n", rblock-block_num);
+      return fsr(rblock-block_num);
    }
    return true;
 }
@@ -1495,62 +1566,50 @@ reposition_dev(DEVICE *dev, uint32_t file, uint32_t block)
 
 /*
  * Write an end of file on the device
- *   Returns: 0 on success
- *            non-zero on failure
+ *   Returns: true on success
+ *            false on failure
  */
-int
-weof_dev(DEVICE *dev, int num)
+bool DEVICE::weof(int num)
 {
    struct mtop mt_com;
    int stat;
-   Dmsg0(29, "weof_dev\n");
+   Dmsg0(129, "weof_dev\n");
    
-   if (dev->fd < 0) {
-      dev->dev_errno = EBADF;
-      Mmsg0(dev->errmsg, _("Bad call to weof_dev. Device not open\n"));
-      Emsg0(M_FATAL, 0, dev->errmsg);
-      return -1;
+   if (fd < 0) {
+      dev_errno = EBADF;
+      Mmsg0(errmsg, _("Bad call to weof_dev. Device not open\n"));
+      Emsg0(M_FATAL, 0, errmsg);
+      return false;
    }
-   dev->file_size = 0;
+   file_size = 0;
 
-   if (!dev->is_tape()) {
-      return 0;
+   if (!is_tape()) {
+      return true;
    }
-   if (!dev->can_append()) {
-      Mmsg0(dev->errmsg, _("Attempt to WEOF on non-appendable Volume\n"));
-      Emsg0(M_FATAL, 0, dev->errmsg);
-      return -1;
+   if (!can_append()) {
+      Mmsg0(errmsg, _("Attempt to WEOF on non-appendable Volume\n"));
+      Emsg0(M_FATAL, 0, errmsg);
+      return false;
    }
       
-   dev->state &= ~(ST_EOT | ST_EOF);  /* remove EOF/EOT flags */
+   clear_eof();
+   clear_eot();
    mt_com.mt_op = MTWEOF;
    mt_com.mt_count = num;
-   stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
+   stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    if (stat == 0) {
-      dev->block_num = 0;
-      dev->file += num;
-      dev->file_addr = 0;
+      block_num = 0;
+      file += num;
+      file_addr = 0;
    } else {
       berrno be;
-      clrerror_dev(dev, MTWEOF);
+      clrerror(MTWEOF);
       if (stat == -1) {
-         Mmsg2(dev->errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"),
-            dev->print_name(), be.strerror());
+         Mmsg2(errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"),
+            print_name(), be.strerror());
        }
    }
-   return stat;
-}
-
-/*
- * Return string message with last error in English
- *  Be careful not to call this routine from within dev.c
- *  while editing an Mmsg() or you will end up in a recursive
- *  loop creating a Segmentation Violation.
- */
-char *
-strerror_dev(DEVICE *dev)
-{
-   return dev->errmsg;
+   return stat == 0;
 }
 
 
@@ -1558,51 +1617,49 @@ strerror_dev(DEVICE *dev)
  * If implemented in system, clear the tape
  * error status.
  */
-void
-clrerror_dev(DEVICE *dev, int func)
+void DEVICE::clrerror(int func)
 {
    const char *msg = NULL;
    struct mtget mt_stat;
    char buf[100];
 
-   dev->dev_errno = errno;         /* save errno */
+   dev_errno = errno;         /* save errno */
    if (errno == EIO) {
-      dev->VolCatInfo.VolCatErrors++;
+      VolCatInfo.VolCatErrors++;
    }
 
-   if (!dev->is_tape()) {
+   if (!is_tape()) {
       return;
    }
    if (errno == ENOTTY || errno == ENOSYS) { /* Function not implemented */
       switch (func) {
       case -1:
-         Emsg0(M_ABORT, 0, _("Got ENOTTY on read/write!\n"));
-         break;
+         break;              /* ignore message printed later */
       case MTWEOF:
          msg = "WTWEOF";
-         dev->capabilities &= ~CAP_EOF; /* turn off feature */
+         capabilities &= ~CAP_EOF; /* turn off feature */
          break;
 #ifdef MTEOM
       case MTEOM:
          msg = "WTEOM";
-         dev->capabilities &= ~CAP_EOM; /* turn off feature */
+         capabilities &= ~CAP_EOM; /* turn off feature */
          break;
 #endif
       case MTFSF:
          msg = "MTFSF";
-         dev->capabilities &= ~CAP_FSF; /* turn off feature */
+         capabilities &= ~CAP_FSF; /* turn off feature */
          break;
       case MTBSF:
          msg = "MTBSF";
-         dev->capabilities &= ~CAP_BSF; /* turn off feature */
+         capabilities &= ~CAP_BSF; /* turn off feature */
          break;
       case MTFSR:
          msg = "MTFSR";
-         dev->capabilities &= ~CAP_FSR; /* turn off feature */
+         capabilities &= ~CAP_FSR; /* turn off feature */
          break;
       case MTBSR:
          msg = "MTBSR";
-         dev->capabilities &= ~CAP_BSR; /* turn off feature */
+         capabilities &= ~CAP_BSR; /* turn off feature */
          break;
       case MTREW:
          msg = "MTREW";
@@ -1622,19 +1679,39 @@ clrerror_dev(DEVICE *dev, int func)
          msg = "MTSRSZ";
          break;
 #endif
+#ifdef MTLOAD
+      case MTLOAD:
+         msg = "MTLOAD";
+         break;
+#endif
+#ifdef MTUNLOCK
+      case MTUNLOCK:
+         msg = "MTUNLOCK";
+         break;
+#endif
+      case MTOFFL:
+         msg = "MTOFFL";
+         break;
       default:
          bsnprintf(buf, sizeof(buf), _("unknown func code %d"), func);
          msg = buf;
          break;
       }
       if (msg != NULL) {
-         dev->dev_errno = ENOSYS;
-         Mmsg1(dev->errmsg, _("I/O function \"%s\" not supported on this device.\n"), msg);
-         Emsg0(M_ERROR, 0, dev->errmsg);
+         dev_errno = ENOSYS;
+         Mmsg1(errmsg, _("I/O function \"%s\" not supported on this device.\n"), msg);
+         Emsg0(M_ERROR, 0, errmsg);
       }
    }
+
+   /*
+    * Now we try different methods of clearing the error
+    *  status on the drive so that it is not locked for
+    *  further operations.
+    */
+
    /* On some systems such as NetBSD, this clears all errors */
-   ioctl(dev->fd, MTIOCGET, (char *)&mt_stat);
+   tape_ioctl(fd, MTIOCGET, (char *)&mt_stat);
 
 /* Found on Linux */
 #ifdef MTIOCLRERR
@@ -1643,7 +1720,7 @@ clrerror_dev(DEVICE *dev, int func)
    mt_com.mt_op = MTIOCLRERR;
    mt_com.mt_count = 1;
    /* Clear any error condition on the tape */
-   ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
+   tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    Dmsg0(200, "Did MTIOCLRERR\n");
 }
 #endif
@@ -1651,11 +1728,12 @@ clrerror_dev(DEVICE *dev, int func)
 /* Typically on FreeBSD */
 #ifdef MTIOCERRSTAT
 {
+  berrno be;
    /* Read and clear SCSI error status */
    union mterrstat mt_errstat;
-   Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev->dev_errno,
-      strerror(dev->dev_errno));
-   ioctl(dev->fd, MTIOCERRSTAT, (char *)&mt_errstat);
+   Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev_errno,
+      be.strerror(dev_errno));
+   tape_ioctl(fd, MTIOCERRSTAT, (char *)&mt_errstat);
 }
 #endif
 
@@ -1666,21 +1744,12 @@ clrerror_dev(DEVICE *dev, int func)
    mt_com.mt_op = MTCSE;
    mt_com.mt_count = 1;
    /* Clear any error condition on the tape */
-   ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
+   tape_ioctl(fd, MTIOCTOP, (char *)&mt_com);
    Dmsg0(200, "Did MTCSE\n");
 }
 #endif
 }
 
-/*
- * Flush buffer contents
- *  No longer used.
- */
-int flush_dev(DEVICE *dev)
-{
-   return 1;
-}
-
 /*
  * Close the device
  */
@@ -1691,8 +1760,14 @@ void DEVICE::close()
       offline();
    }
    if (fd >= 0) {
-      ::close(fd);
+      if (is_tape()) {
+         tape_close(fd);
+      } else {
+         ::close(fd);
+      }
    } else {
+      Dmsg2(100, "device %s already closed vol=%s\n", print_name(),
+         VolHdr.VolumeName);
       return;                         /* already closed */
    }
 
@@ -1701,17 +1776,26 @@ void DEVICE::close()
    }
    
    /* Remove the last part file if it is empty */
-   if (num_parts > 0) {
+   if (num_dvd_parts > 0) {
       struct stat statp;
+      int part_save = part;
       POOL_MEM archive_name(PM_FNAME);
-      part = num_parts;
-      Dmsg1(100, "Call make_dvd_filename. Vol=%s\n", VolCatInfo.VolCatName);
+      int status;
+
+      part = num_dvd_parts;
       make_spooled_dvd_filename(this, archive_name);
       /* Check that the part file is empty */
-      if ((stat(archive_name.c_str(), &statp) == 0) && (statp.st_size == 0)) {
+      status = stat(archive_name.c_str(), &statp);
+      if (status == 0 && statp.st_size == 0) {
+         Dmsg3(100, "Unlink empty part in close call make_dvd_filename. part=%d num=%d vol=%s\n", 
+                part, num_dvd_parts, VolCatInfo.VolCatName);
          Dmsg1(100, "unlink(%s)\n", archive_name.c_str());
          unlink(archive_name.c_str());
-      }
+         set_part_spooled(false);        /* no spooled part left */
+      } else if (status < 0) {                         
+         set_part_spooled(false);        /* spool doesn't exit */
+      }       
+      part = part_save;               /* restore part number */
    }
    
    /* Clean up device packet so it can be reused */
@@ -1721,13 +1805,10 @@ void DEVICE::close()
    file = block_num = 0;
    file_size = 0;
    file_addr = 0;
-   part = 0;
-   num_parts = 0;
-   part_size = 0;
-   part_start = 0;
    EndFile = EndBlock = 0;
-   memset(&VolCatInfo, 0, sizeof(VolCatInfo));
+   Slot = -1;             /* unknown slot */
    free_volume(this);
+   memset(&VolCatInfo, 0, sizeof(VolCatInfo));
    memset(&VolHdr, 0, sizeof(VolHdr));
    if (tid) {
       stop_thread_timer(tid);
@@ -1736,6 +1817,30 @@ void DEVICE::close()
    openmode = 0;
 }
 
+/*
+ * This call closes the device, but it is used in DVD handling
+ *  where we close one part and then open the next part. The
+ *  difference between close_part() and close() is that close_part()
+ *  saves the state information of the device (e.g. the Volume lable,
+ *  the Volume Catalog record, ...  This permits opening and closing
+ *  the Volume parts multiple times without losing track of what the    
+ *  main Volume parameters are.
+ */
+void DEVICE::close_part(DCR *dcr)
+{
+   VOLUME_LABEL saveVolHdr;
+   VOLUME_CAT_INFO saveVolCatInfo;     /* Volume Catalog Information */
+
+
+   memcpy(&saveVolHdr, &VolHdr, sizeof(saveVolHdr));
+   memcpy(&saveVolCatInfo, &VolCatInfo, sizeof(saveVolCatInfo));
+   close();                           /* close current part */
+   memcpy(&VolHdr, &saveVolHdr, sizeof(VolHdr));
+   memcpy(&VolCatInfo, &saveVolCatInfo, sizeof(VolCatInfo));
+   memcpy(&dcr->VolCatInfo, &saveVolCatInfo, sizeof(dcr->VolCatInfo));
+}
+
+
 
 
 bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
@@ -1765,7 +1870,7 @@ bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */
  */
 bool DEVICE::mount(int timeout) 
 {
-   Dmsg0(90, "Enter mount\n");
+   Dmsg0(190, "Enter mount\n");
    if (is_mounted()) {
       return true;
    } else if (requires_mount()) {
@@ -1822,10 +1927,12 @@ bool DEVICE::do_mount(int mount, int dotimeout)
    }
    results = get_memory(2000);
    results[0] = 0;
+
    /* If busy retry each second */
+   Dmsg1(20, "do_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 internationalisation (This is not a problem) */
+      /* Doesn't work with internationalization (This is not a problem) */
       if (fnmatch("*is already mounted on", results, 0) == 0) {
          break;
       }
@@ -1936,8 +2043,12 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg)
             str = dev_name;
             break;
          case 'e':
-            if (num_parts == 0) {
-               str = "1";
+            if (num_dvd_parts == 0) {
+               if (truncating || truncated_dvd) {
+                  str = "2";
+               } else {
+                  str = "1";
+               }
             } else {
                str = "0";
             }
@@ -1999,41 +2110,35 @@ uint32_t dev_file(DEVICE *dev)
 /*
  * Free memory allocated for the device
  */
-void
-term_dev(DEVICE *dev)
+void DEVICE::term(void)
 {
-   if (!dev) {
-      dev->dev_errno = EBADF;
-      Mmsg0(dev->errmsg, _("Bad call to term_dev. Device not open\n"));
-      Emsg0(M_FATAL, 0, dev->errmsg);
-      return;
-   }
-   Dmsg1(29, "term_dev: %s\n", dev->print_name());
-   dev->close();
-   if (dev->dev_name) {
-      free_memory(dev->dev_name);
-      dev->dev_name = NULL;
-   }
-   if (dev->prt_name) {
-      free_memory(dev->prt_name);
-      dev->prt_name = NULL;
-   }
-   if (dev->errmsg) {
-      free_pool_memory(dev->errmsg);
-      dev->errmsg = NULL;
-   }
-   pthread_mutex_destroy(&dev->mutex);
-   pthread_cond_destroy(&dev->wait);
-   pthread_cond_destroy(&dev->wait_next_vol);
-   pthread_mutex_destroy(&dev->spool_mutex);
-   rwl_destroy(&dev->lock);
-   if (dev->attached_dcrs) {
-      delete dev->attached_dcrs;
-      dev->attached_dcrs = NULL;
-   }
-   if (dev->state & ST_MALLOC) {
-      free((char *)dev);
-   }
+   Dmsg1(900, "term dev: %s\n", print_name());
+   close();
+   if (dev_name) {
+      free_memory(dev_name);
+      dev_name = NULL;
+   }
+   if (prt_name) {
+      free_memory(prt_name);
+      prt_name = NULL;
+   }
+   if (errmsg) {
+      free_pool_memory(errmsg);
+      errmsg = NULL;
+   }
+   pthread_mutex_destroy(&mutex);
+   pthread_cond_destroy(&wait);
+   pthread_cond_destroy(&wait_next_vol);
+   pthread_mutex_destroy(&spool_mutex);
+   rwl_destroy(&lock);
+   if (attached_dcrs) {
+      delete attached_dcrs;
+      attached_dcrs = NULL;
+   }
+   if (device) {
+      device->dev = NULL;
+   }
+   free((char *)this);
 }
 
 /*
@@ -2098,14 +2203,14 @@ bool double_dev_wait_time(DEVICE *dev)
 
 void set_os_device_parameters(DEVICE *dev)
 {
-#ifdef HAVE_LINUX_OS
+#if defined(HAVE_LINUX_OS) || defined(HAVE_WIN32)
    struct mtop mt_com;
    if (dev->min_block_size == dev->max_block_size &&
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSETBLK;
       mt_com.mt_count = 0;
-      if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTSETBLK);
+      if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         dev->clrerror(MTSETBLK);
       }
       mt_com.mt_op = MTSETDRVBUFFER;
       mt_com.mt_count = MT_ST_CLEARBOOLEANS;
@@ -2115,8 +2220,8 @@ void set_os_device_parameters(DEVICE *dev)
       if (dev->has_cap(CAP_EOM)) {
          mt_com.mt_count |= MT_ST_FAST_MTEOM;
       }
-      if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTSETBLK);
+      if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         dev->clrerror(MTSETBLK);
       }
    }
    return;
@@ -2128,14 +2233,14 @@ void set_os_device_parameters(DEVICE *dev)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSETBSIZ;
       mt_com.mt_count = 0;
-      if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTSETBSIZ);
+      if (tape_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 (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTEWARN);
+      if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         dev->clrerror(MTEWARN);
       }
    }
    return;
@@ -2147,8 +2252,8 @@ void set_os_device_parameters(DEVICE *dev)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSETBSIZ;
       mt_com.mt_count = 0;
-      if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTSETBSIZ);
+      if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         dev->clrerror(MTSETBSIZ);
       }
    }
    return;
@@ -2160,8 +2265,8 @@ void set_os_device_parameters(DEVICE *dev)
        dev->min_block_size == 0) {    /* variable block mode */
       mt_com.mt_op = MTSRSZ;
       mt_com.mt_count = 0;
-      if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
-         clrerror_dev(dev, MTSRSZ);
+      if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) {
+         dev->clrerror(MTSRSZ);
       }
    }
    return;
@@ -2171,7 +2276,7 @@ void set_os_device_parameters(DEVICE *dev)
 static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat)
 {
    return dev->has_cap(CAP_MTIOCGET) && 
-          ioctl(dev->fd, MTIOCGET, (char *)mt_stat) == 0 &&
+          tape_ioctl(dev->fd, MTIOCGET, (char *)mt_stat) == 0 &&
           mt_stat->mt_fileno >= 0;
 }
 
@@ -2185,5 +2290,10 @@ static char *modes[] = {
 
 static char *mode_to_str(int mode)  
 {
+   static char buf[100];
+   if (mode < 1 || mode > 4) {
+      bsnprintf(buf, sizeof(buf), "BAD mode=%d", mode);
+      return buf;
+    }
    return modes[mode-1];
 }