}
/*
- * 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.
*/
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;
}
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 {
}
}
- 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));
/* 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)) {
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 */
}
-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 */
}
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) {
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;
}
}
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 */
* 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)
{
{
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);
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
str = dev->dev_name;
break;
case 'e':
- if (dev->part == 0 && !dev->is_mounted()) {
+ if (dev->part == 0) {
str = "1";
} else {
str = "0";