From 81096de5e30effc66a88c0facddc0481d2e68a68 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 27 Jul 2005 22:47:39 +0000 Subject: [PATCH] Update/bugfixes in DVD-writing. (@kern, please also check dev.c:truncate_dev) git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2263 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/src/stored/dev.c | 49 +++++++++++---- bacula/src/stored/dvd.c | 131 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 163 insertions(+), 17 deletions(-) diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 439e353b9b..48cc89505c 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -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) { diff --git a/bacula/src/stored/dvd.c b/bacula/src/stored/dvd.c index 56b7e25761..2670b5cb68 100644 --- a/bacula/src/stored/dvd.c +++ b/bacula/src/stored/dvd.c @@ -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"; -- 2.39.5