]> git.sur5r.net Git - bacula/bacula/commitdiff
Update/bugfixes in DVD-writing. (@kern, please also check dev.c:truncate_dev)
authorNicolas Boichat <nicolas@boichat.ch>
Wed, 27 Jul 2005 22:47:39 +0000 (22:47 +0000)
committerNicolas Boichat <nicolas@boichat.ch>
Wed, 27 Jul 2005 22:47:39 +0000 (22:47 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2263 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/src/stored/dev.c
bacula/src/stored/dvd.c

index 439e353b9ba5827f5af0e65efa3b0fb252cfa11b..48cc89505c5a6d6d078cbfa48627e393c275323c 100644 (file)
@@ -445,7 +445,7 @@ void DEVICE::open_file_device(int omode)
 }
 
 /*
- * Open a DVD device. N.B. at this point, dcr->VolCatInfo.VolCatName
+ * 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.
  */
@@ -461,6 +461,8 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
          archive_name.c_str(), mode_to_str(omode));
 
    if (VolCatInfo.VolCatName[0] == 0) {
+      Dmsg1(10,  "Could not open file device %s. No Volume name given.\n",
+         print_name());
       Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
          print_name());
       fd = -1;
@@ -472,9 +474,29 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
    }
    part_size = 0;
    
-
-   if (!mount_dev(this, 1)) {
+   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 (mount_dev(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_dev 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);
+            fd = -1;
+            return;
+         }
+      }
+   }
+   else {
+      /* We cannot mount the device */
       if (num_parts == 0) {
+         /* Run free space, check there is a media. */
          Dmsg1(29, "Could not mount device %s, this is not a problem (num_parts == 0).\n", print_name());
       }
       else {
@@ -485,9 +507,9 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
       }
    }
    
-   Dmsg5(29, "open dev: %s dev=%s mode=%s part=%d npart=%d\n", 
+   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);
+      part, num_parts, dcr->VolCatInfo.VolCatParts);
    openmode = omode;
    Dmsg2(100, "openmode=%d %s\n", openmode, mode_to_str(openmode));
    
@@ -510,9 +532,9 @@ void DEVICE::open_dvd_device(DCR *dcr, int omode)
    /* Use system open() */
    if ((fd = ::open(archive_name.c_str(), mode, 0640)) < 0) {
       berrno be;
-      dev_errno = errno;
       Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), 
             be.strerror());
+      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)) {
@@ -587,10 +609,12 @@ bool rewind_dev(DEVICE *dev)
 
    Dmsg2(29, "rewind_dev fd=%d %s\n", dev->fd, dev->print_name());
    if (dev->fd < 0) {
-      dev->dev_errno = EBADF;
-      Mmsg1(dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"),
+      if (!dev->is_dvd()) { /* In case of major error, the fd is not open on DVD, so we don't want to abort. */
+         dev->dev_errno = EBADF;
+         Mmsg1(dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"),
             dev->print_name());
-      Emsg0(M_ABORT, 0, dev->errmsg);
+         Emsg0(M_ABORT, 0, dev->errmsg);
+      }
       return false;
    }
    dev->state &= ~(ST_EOT|ST_EOF|ST_WEOT);  /* remove EOF/EOT flags */
@@ -1701,8 +1725,10 @@ void DEVICE::close()
 }
 
 
-bool truncate_dev(DEVICE *dev)
+bool truncate_dev(DCR *dcr) /* We need the DCR for DVD-writing */
 {
+   DEVICE *dev = dcr->dev;
+
    Dmsg1(100, "truncate_dev %s\n", dev->print_name());
    if (dev->is_tape()) {
       return true;                    /* we don't really truncate tapes */
@@ -1710,8 +1736,7 @@ bool truncate_dev(DEVICE *dev)
    }
    
    if (dev->is_dvd()) {
-      Mmsg1(dev->errmsg, _("Truncate DVD %s not supported.\n"), dev->print_name());
-      return false;   /* we cannot truncate DVDs */
+      return truncate_dvd_dev(dcr);
    }
    
    if (ftruncate(dev->fd, 0) != 0) {
index 56b7e257611c1c345764a49877cdbbcf1aef11e7..2670b5cb68b4246e50131705a8850d0184380095 100644 (file)
@@ -176,8 +176,8 @@ static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout)
       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
       while (1) {
          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
-            dev->dev_errno = ENOENT;
-            Dmsg2(29, "open_mounted_dev: failed to find suitable file in dir %s (dev=%s)\n", 
+            dev->dev_errno = EIO;
+            Dmsg2(129, "open_mounted_dev: failed to find suitable file in dir %s (dev=%s)\n", 
                   dev->device->mount_point, dev->print_name());
             break;
          }
@@ -185,6 +185,9 @@ static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout)
       }
       free(entry);
       closedir(dp);
+      
+      Dmsg1(29, "open_mounted_dev: got %d files in the mount point\n", count);
+      
       if (count > 2) {
          mount = 1;                      /* If we got more than . and .. */
          break;                          /*   there must be something mounted */
@@ -284,6 +287,7 @@ void update_free_space_dev(DEVICE* dev)
  * Write a part (Vol, Vol.1, ...) from the spool to the DVD   
  * This MUST only be called from open_next_part. Otherwise the part number
  * is not updated.
+ * It is also called from truncate_dvd_dev to "blank" the medium.
  */
 static bool dvd_write_part(DCR *dcr) 
 {
@@ -457,8 +461,8 @@ int open_first_part(DCR *dcr, int mode)
 {
    DEVICE *dev = dcr->dev;
 
-   Dmsg3(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d\n", dev->print_name(), 
-         dev->VolCatInfo.VolCatName, dev->openmode);
+   Dmsg4(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_parts=%d\n", dev->print_name(), 
+         dev->VolCatInfo.VolCatName, dev->openmode, dev->num_parts);
 
    if (dev->fd >= 0) {
       close(dev->fd);
@@ -637,6 +641,123 @@ bool dvd_close_job(DCR *dcr)
    return ok;
 }
 
+bool truncate_dvd_dev(DCR *dcr) {
+   DEVICE* dev = dcr->dev;
+
+   /* Set num_parts to zero (on disk) */
+   dev->num_parts = 0;
+   dcr->VolCatInfo.VolCatParts = 0;
+   dev->VolCatInfo.VolCatParts = 0;
+   
+   Dmsg0(100, "truncate_dvd_dev: Opening first part (1)...\n");
+   
+   dev->truncating = true;
+   if (open_first_part(dcr, OPEN_READ_WRITE) < 0) {
+      Dmsg0(100, "truncate_dvd_dev: Error while opening first part (1).\n");
+      dev->truncating = false;
+      return false;
+   }
+   dev->truncating = false;
+
+   Dmsg0(100, "truncate_dvd_dev: Truncating...\n");
+
+   /* If necessary, truncate it. */
+   if (ftruncate(dev->fd, 0) != 0) {
+      berrno be;
+      Mmsg2(dev->errmsg, _("Unable to truncate device %s. ERR=%s\n"), 
+         dev->print_name(), be.strerror());
+      return false;
+   }
+   
+   close(dev->fd);
+   dev->fd = -1;
+   dev->clear_opened();
+   
+   Dmsg0(100, "truncate_dvd_dev: Opening first part (2)...\n");
+   
+   if (!dvd_write_part(dcr)) {
+      Dmsg0(100, "truncate_dvd_dev: Error while writing to DVD.\n");
+      return false;
+   }
+   
+   if (open_first_part(dcr, OPEN_READ_WRITE) < 0) {
+      Dmsg0(100, "truncate_dvd_dev: Error while opening first part (2).\n");
+      return false;
+   }
+
+   return true;
+}
+
+/* Checks if we can write on a non-blank DVD: meaning that it just have been
+ * truncated (there is only one zero-sized file on the DVD, with the right
+ * volume name). */
+bool check_can_write_on_non_blank_dvd(DCR *dcr) {
+   DEVICE* dev = dcr->dev;
+   DIR* dp;
+   struct dirent *entry, *result;
+   int name_max;
+   int count = 0;
+   int matched = 0; /* We found an empty file with the right name. */
+   struct stat filestat;
+      
+   name_max = pathconf(".", _PC_NAME_MAX);
+   if (name_max < 1024) {
+      name_max = 1024;
+   }
+         
+   if (!(dp = opendir(dev->device->mount_point))) {
+      berrno be;
+      dev->dev_errno = errno;
+      Dmsg3(29, "check_can_write_on_non_blank_dvd: failed to open dir %s (dev=%s), ERR=%s\n", 
+            dev->device->mount_point, dev->print_name(), be.strerror());
+      return false;
+   }
+   
+   entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
+   while (1) {
+      if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
+         dev->dev_errno = EIO;
+         Dmsg2(129, "check_can_write_on_non_blank_dvd: failed to find suitable file in dir %s (dev=%s)\n", 
+               dev->device->mount_point, dev->print_name());
+         break;
+      }
+      else {
+         Dmsg2(99, "check_can_write_on_non_blank_dvd: found %s (versus %s)\n", 
+               result->d_name, dev->VolCatInfo.VolCatName);
+         if (strcmp(result->d_name, dev->VolCatInfo.VolCatName) == 0) {
+            /* Found the file, checking it is empty */
+            POOL_MEM filename(PM_FNAME);
+            pm_strcpy(filename, dev->device->mount_point);
+            if (filename.c_str()[strlen(filename.c_str())-1] != '/') {
+               pm_strcat(filename, "/");
+            }
+            pm_strcat(filename, dev->VolCatInfo.VolCatName);
+            if (stat(filename.c_str(), &filestat) < 0) {
+               berrno be;
+               dev->dev_errno = errno;
+               Dmsg2(29, "check_can_write_on_non_blank_dvd: cannot stat file (file=%s), ERR=%s\n", 
+                  filename.c_str(), be.strerror());
+               return false;
+            }
+            Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %d\n", 
+               filename.c_str(), filestat.st_size);
+            matched = (filestat.st_size == 0);
+         }
+      }
+      count++;
+   }
+   free(entry);
+   closedir(dp);
+   
+   Dmsg2(29, "check_can_write_on_non_blank_dvd: got %d files in the mount point (matched=%d)\n", count, matched);
+   
+   if (count != 3) {
+      /* There is more than 3 files (., .., and the volume file) */
+      return false;
+   }
+   
+   return matched;
+}
 
 /*
  * Edit codes into (Un)MountCommand, Write(First)PartCommand
@@ -670,7 +791,7 @@ static void edit_device_codes_dev(DEVICE* dev, POOL_MEM &omsg, const char *imsg)
             str = dev->dev_name;
             break;
          case 'e':
-            if (dev->part == 0 && !dev->is_mounted()) {
+            if (dev->part == 0) {
                str = "1";
             } else {
                str = "0";