]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/dev.c
Apply patch from Richard Mortimer to ensure that the number
[bacula/bacula] / bacula / src / stored / dev.c
index 9dfaa307d0214efe691d028af1c22e9cf492af6d..a4dc8477339001af4280534e89e1f5f05008078f 100644 (file)
@@ -131,6 +131,7 @@ init_dev(JCR *jcr, DEVRES *device)
 
    dev = (DEVICE *)malloc(sizeof(DEVICE));
    memset(dev, 0, sizeof(DEVICE));
+   dev->Slot = -1;       /* unknown */
 
    /* Copy user supplied device parameters from Resource */
    dev->dev_name = get_memory(strlen(device->device_name)+1);
@@ -281,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);
@@ -292,7 +294,7 @@ DEVICE::open(DCR *dcr, int omode)
       open_file_device(dcr, omode);
    }
    state |= preserve;                 /* reset any important state info */
-   Dmsg1(100, "preserve=0x%x\n", preserve);
+   Dmsg2(100, "preserve=0x%x fd=%d\n", preserve, fd);
    return fd;
 }
 
@@ -325,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;
@@ -341,7 +341,7 @@ void DEVICE::open_tape_device(DCR *dcr, int omode)
       mode_to_str(omode), nonblocking);
    /* Use system open() */
 
-#ifdef HAVE_WIN32
+#if defined(HAVE_WIN32)
    if ((fd = tape_open(dev_name, mode)) < 0) {
       berrno be;
       dev_errno = errno;
@@ -423,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)
@@ -434,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) {
@@ -471,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);
 }
 
 /*
@@ -491,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());
@@ -501,25 +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;
+   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;
 
-   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;
-   }
-
+   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);
@@ -530,21 +543,24 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
          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();
@@ -552,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));
    
@@ -562,12 +578,15 @@ 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 {
+      set_part_spooled(false);
+   } else {
+      omode = OPEN_READ_WRITE;
       make_spooled_dvd_filename(this, archive_name);
+      set_part_spooled(true);
    }
    set_mode(omode);
 
@@ -583,27 +602,26 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
       dev_errno = EIO; /* Interpreted as no device present by acquire.c:acquire_device_for_read(). */
       Dmsg1(29, "open failed: %s", errmsg);
       
+      /* Previous open failed. See if we can recover */
       if ((omode == OPEN_READ_ONLY || omode == OPEN_READ_WRITE) &&
-          (part == num_parts)) {
-         /* If the last part (on spool), doesn't exists when accessing,
+          (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. */
-         Dmsg0(29, "Creating last part on spool to make our caller happy\n");
+         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(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;
@@ -618,19 +636,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++;
-            }
-         }*/
       }
    }
 }
@@ -648,6 +653,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;
@@ -657,10 +666,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;
@@ -1157,7 +1162,6 @@ 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;
@@ -1774,17 +1778,30 @@ void DEVICE::close()
    }
    
    /* Remove the last part file if it is empty */
-   if (num_parts > 0) {
+   if (num_dvd_parts > 0) {
       struct stat statp;
+      uint32_t 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());
-      }
+         if (part_save == part) {
+           set_part_spooled(false);        /* no spooled part left */
+         }
+      } else if (status < 0) {                         
+         if (part_save == part) {
+           set_part_spooled(false);        /* spool doesn't exit */
+         }
+      }       
+      part = part_save;               /* restore part number */
    }
    
    /* Clean up device packet so it can be reused */
@@ -1794,11 +1811,8 @@ 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;
+   Slot = -1;             /* unknown slot */
    free_volume(this);
    memset(&VolCatInfo, 0, sizeof(VolCatInfo));
    memset(&VolHdr, 0, sizeof(VolHdr));
@@ -1809,6 +1823,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 */
@@ -1895,10 +1933,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;
       }
@@ -2009,7 +2049,7 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg)
             str = dev_name;
             break;
          case 'e':
-            if (num_parts == 0) {
+            if (num_dvd_parts == 0) {
                if (truncating || truncated_dvd) {
                   str = "2";
                } else {
@@ -2256,5 +2296,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];
 }