X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fdvd.c;h=5ae09df2c2578e0ba83db5dec8da8612fa5c8679;hb=22a5d43f2bf48e4d5c056dc17bd7cff90d84290e;hp=298668240ddde8e329d4ca4347ccc6f3a9f6323c;hpb=eaa5e0915e2cc73aec3d8383fe73a9689490d661;p=bacula%2Fbacula diff --git a/bacula/src/stored/dvd.c b/bacula/src/stored/dvd.c index 298668240d..5ae09df2c2 100644 --- a/bacula/src/stored/dvd.c +++ b/bacula/src/stored/dvd.c @@ -36,7 +36,6 @@ void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) { pm_strcpy(archive_name, dev->device->mount_point); add_file_and_part_name(dev, archive_name); - dev->set_part_spooled(false); } void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) @@ -48,12 +47,12 @@ void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) pm_strcpy(archive_name, working_directory); } add_file_and_part_name(dev, archive_name); - dev->set_part_spooled(true); } static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name) { char partnumber[20]; + if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') { pm_strcat(archive_name, "/"); } @@ -65,7 +64,8 @@ static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name) bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part); pm_strcat(archive_name, partnumber); } - Dmsg1(400, "Exit add_file_part_name: arch=%s\n", archive_name.c_str()); + Dmsg2(400, "Exit add_file_part_name: arch=%s, part=%d\n", + archive_name.c_str(), dev->part); } /* Mount the device. @@ -122,6 +122,7 @@ static bool do_mount_dvd(DEVICE* dev, int mount, int dotimeout) icmd = dev->device->unmount_command; } + dev->clear_freespace_ok(); dev->edit_mount_codes(ocmd, icmd); Dmsg2(200, "do_mount_dvd: cmd=%s mounted=%d\n", ocmd.c_str(), !!dev->is_mounted()); @@ -138,7 +139,7 @@ static bool do_mount_dvd(DEVICE* dev, int mount, int dotimeout) Dmsg1(20, "Run mount prog=%s\n", ocmd.c_str()); while ((status = run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results)) != 0) { - Dmsg2(99, "Mount status=%d result=%s\n", status, results); + Dmsg2(20, "Mount status=%d result=%s\n", status, results); /* Doesn't work with internationalization (This is not a problem) */ if (mount && fnmatch("*is already mounted on*", results, 0) == 0) { break; @@ -261,6 +262,10 @@ bool update_free_space_dev(DEVICE* dev) char ed1[50]; bool ok = false; int status; + + if (!dev->is_dvd() || dev->is_freespace_ok()) { + return true; + } /* The device must be mounted in order to dvd-freespace to work */ mount_dvd(dev, 1); @@ -271,7 +276,7 @@ bool update_free_space_dev(DEVICE* dev) if (!icmd) { dev->free_space = 0; dev->free_space_errno = 0; - dev->clear_freespace_ok(); /* No valid freespace */ + dev->clear_freespace_ok(); /* No valid freespace */ dev->clear_media(); Dmsg2(29, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", edit_uint64(dev->free_space, ed1), dev->free_space_errno); @@ -292,6 +297,7 @@ bool update_free_space_dev(DEVICE* dev) berrno be; Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str()); status = run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results); + Dmsg2(500, "Freespace status=%d result=%s\n", status, results); if (status == 0) { free = str_to_int64(results); Dmsg1(400, "Free space program run: Freespace=%s\n", results); @@ -352,7 +358,8 @@ bool dvd_write_part(DCR *dcr) DEVICE *dev = dcr->dev; POOL_MEM archive_name(PM_FNAME); - /* Don't write empty part files. + /* + * Don't write empty part files. * This is only useful when growisofs does not support write beyond * the 4GB boundary. * Example : @@ -364,16 +371,16 @@ bool dvd_write_part(DCR *dcr) * - Bacula thinks he must finish to write to the device, so it * tries to write the last part (0-byte), but dvd-writepart fails... * - * ***FIXME**** we cannot write a blank part!!!!!!! * There is one exception: when recycling a volume, we write a blank part * file, so, then, we need to accept to write it. */ - if (dev->part_size == 0) { + if (dev->part_size == 0 && !dev->truncating) { Dmsg2(29, "dvd_write_part: device is %s, won't write blank part %d\n", dev->print_name(), dev->part); /* Delete spool file */ make_spooled_dvd_filename(dev, archive_name); unlink(archive_name.c_str()); - Dmsg1(29, "unlink(%s)\n", archive_name.c_str()); + dev->set_part_spooled(false); + Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str()); sm_check(__FILE__, __LINE__, false); return true; } @@ -385,6 +392,8 @@ bool dvd_write_part(DCR *dcr) int timeout; char ed1[50]; + dev->clear_freespace_ok(); /* need to update freespace */ + sm_check(__FILE__, __LINE__, false); Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted()); icmd = dev->device->write_part_command; @@ -409,41 +418,44 @@ bool dvd_write_part(DCR *dcr) * in case of a serious emergency. */ - if (dev->part == 1) + if (dev->part == 1) { timeout = 16000; - else + } else { timeout = dev->max_open_wait + (dev->part_size/(1350*1024/4)); + } - Dmsg2(20, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout); + Dmsg2(20, "Write part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout); status = run_program_full_output(ocmd.c_str(), timeout, results.c_str()); + Dmsg2(20, "Write part status=%d result=%s\n", status, results.c_str()); + dev->blank_dvd = false; if (status != 0) { Jmsg2(dcr->jcr, M_FATAL, 0, _("Error writing part %d to the DVD: ERR=%s\n"), dev->part, results.c_str()); Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"), results.c_str()); - Dmsg1(000, "%s\n", dev->errmsg); + Dmsg1(100, "%s\n", dev->errmsg); dev->dev_errno = EIO; - mark_volume_in_error(dcr); + if (!dev->truncating) { + mark_volume_in_error(dcr); + } sm_check(__FILE__, __LINE__, false); - dev->truncated_dvd = false; return false; } - Jmsg(dcr->jcr, M_INFO, 0, _("Part %d written to DVD.\n"), dev->part); - Dmsg2(400, "dvd_write_part: Part %d written to DVD\nResults: %s\n", - dev->part, results.c_str()); + Jmsg(dcr->jcr, M_INFO, 0, _("Part %d (%lld bytes) written to DVD.\n"), dev->part, dev->part_size); + Dmsg3(400, "dvd_write_part: Part %d (%lld bytes) written to DVD\nResults: %s\n", + dev->part, dev->part_size, results.c_str()); - if (dev->truncated_dvd) { - dev->truncated_dvd = false; /* turn off flag */ - } else { /* DVD part written */ - dev->num_dvd_parts++; /* there is now one more part on DVD */ - dev->VolCatInfo.VolCatParts = dev->num_dvd_parts; - } + dev->num_dvd_parts++; /* there is now one more part on DVD */ + dev->VolCatInfo.VolCatParts = dev->num_dvd_parts; + dcr->VolCatInfo.VolCatParts = dev->num_dvd_parts; + Dmsg1(100, "Update num_parts=%d\n", dev->num_dvd_parts); /* Delete spool file */ make_spooled_dvd_filename(dev, archive_name); unlink(archive_name.c_str()); - Dmsg1(29, "unlink(%s)\n", archive_name.c_str()); + dev->set_part_spooled(false); + Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str()); sm_check(__FILE__, __LINE__, false); /* growisofs umounted the device, so remount it (it will update the free space) */ @@ -464,13 +476,12 @@ bool dvd_write_part(DCR *dcr) int dvd_open_next_part(DCR *dcr) { DEVICE *dev = dcr->dev; - VOLUME_LABEL VolHdr; Dmsg6(29, "Enter: == open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", dev->part, dev->num_dvd_parts, dev->print_name(), dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr); if (!dev->is_dvd()) { - Dmsg1(000, "Device %s is not dvd!!!!\n", dev->print_name()); + Dmsg1(100, "Device %s is not dvd!!!!\n", dev->print_name()); return -1; } @@ -481,14 +492,7 @@ int dvd_open_next_part(DCR *dcr) return dev->fd; } - /* - * Note, when we close, the Volume header is zeroed. Since - * we reopen, but are in fact opening the same volume without - * re-reading the Volume header, we simply save and restore it. - */ - memcpy(&VolHdr, &dev->VolHdr, sizeof(VolHdr)); - dev->close(); /* close current part */ - memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); + dev->close_part(dcr); /* close current part */ /* * If we have a spooled part open, write it to the @@ -507,6 +511,7 @@ int dvd_open_next_part(DCR *dcr) dev->part++; Dmsg2(29, "Inc part=%d num_dvd_parts=%d\n", dev->part, dev->num_dvd_parts); + /* Are we working on a part past what is written in the DVD? */ if (dev->num_dvd_parts < dev->part) { POOL_MEM archive_name(PM_FNAME); struct stat buf; @@ -514,35 +519,39 @@ int dvd_open_next_part(DCR *dcr) * First check what is on DVD. If our part is there, we * are in trouble, so bail out. * NB: This is however not a problem if we are writing the first part. - * It simply means that we are overriding an existing volume... + * It simply means that we are over writing an existing volume... */ if (dev->num_dvd_parts > 0) { make_mounted_dvd_filename(dev, archive_name); /* makes dvd name */ Dmsg1(100, "Check if part on DVD: %s\n", archive_name.c_str()); if (stat(archive_name.c_str(), &buf) == 0) { /* bad news bail out */ + dev->set_part_spooled(false); Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"), archive_name.c_str()); return -1; } } +#ifdef neeeded Dmsg2(400, "num_dvd_parts=%d part=%d\n", dev->num_dvd_parts, dev->part); make_spooled_dvd_filename(dev, archive_name); /* makes spool name */ /* Check if the next part exists in spool directory . */ - Dmsg1(100, "Check if part on spool: $s\n", archive_name.c_str()); + Dmsg1(100, "Check if part on spool: %s\n", archive_name.c_str()); if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) { - Dmsg1(29, "open_next_part %s is in the way, deleting it...\n", archive_name.c_str()); + Dmsg1(29, "======= Part %s is in the way, deleting it...\n", archive_name.c_str()); /* Then try to unlink it */ if (unlink(archive_name.c_str()) < 0) { berrno be; + dev->set_part_spooled(false); dev->dev_errno = errno; Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), archive_name.c_str(), be.strerror()); return -1; } } +#endif } Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, @@ -552,8 +561,6 @@ int dvd_open_next_part(DCR *dcr) if (dev->open(dcr, OPEN_READ_ONLY) < 0) { return -1; } - /* Restore Volume header record */ - memcpy(&dev->VolHdr, &VolHdr, sizeof(dev->VolHdr)); dev->set_labeled(); /* all next parts are "labeled" */ return dev->fd; @@ -564,23 +571,15 @@ int dvd_open_next_part(DCR *dcr) * - Close the fd * - Reopen the device */ -int dvd_open_first_part(DCR *dcr, int mode) +static bool dvd_open_first_part(DCR *dcr, int mode) { DEVICE *dev = dcr->dev; - VOLUME_LABEL VolHdr; Dmsg5(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_dvd_parts=%d append=%d\n", dev->print_name(), dev->VolCatInfo.VolCatName, dev->openmode, dev->num_dvd_parts, dev->can_append()); - /* - * Note, when we close, the Volume header is zeroed. Since - * we reopen, but are in fact opening the same volume without - * re-reading the Volume header, we simply save and restore it. - */ - memcpy(&VolHdr, &dev->VolHdr, sizeof(VolHdr)); - dev->close(); - memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); + dev->close_part(dcr); Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, mode); @@ -588,37 +587,31 @@ int dvd_open_first_part(DCR *dcr, int mode) dev->part = 1; dev->part_start = 0; - - /* Restore Volume header record */ - memcpy(&dev->VolHdr, &VolHdr, sizeof(dev->VolHdr)); if (dev->open(dcr, mode) < 0) { Dmsg0(400, "open dev() failed\n"); - return -1; + return false; } Dmsg2(400, "Leave open_first_part state=%s append=%d\n", dev->is_open()?"open":"not open", dev->can_append()); - return dev->fd; + return true; } -/* Protected version of lseek, which opens the right part if necessary */ -off_t lseek_dev(DEVICE *dev, off_t offset, int whence) +/* + * Do an lseek on a DVD handling all the different parts + */ +off_t lseek_dvd(DCR *dcr, off_t offset, int whence) { - DCR *dcr; + DEVICE *dev = dcr->dev; off_t pos; char ed1[50], ed2[50]; - Dmsg5(400, "Enter lseek_dev fd=%d off=%s w=%d part=%d nparts=%d\n", dev->fd, + Dmsg5(400, "Enter lseek_dvd fd=%d off=%s w=%d part=%d nparts=%d\n", dev->fd, edit_int64(offset, ed1), whence, dev->part, dev->num_dvd_parts); - if (!dev->is_dvd()) { - Dmsg0(400, "Using sys lseek\n"); - return lseek(dev->fd, offset, whence); - } - dcr = (DCR *)dev->attached_dcrs->first(); /* any dcr will do */ switch(whence) { case SEEK_SET: - Dmsg2(400, "lseek_dev SEEK_SET to %s (part_start=%s)\n", + Dmsg2(400, "lseek_dvd SEEK_SET to %s (part_start=%s)\n", edit_int64(offset, ed1), edit_uint64(dev->part_start, ed2)); if ((uint64_t)offset >= dev->part_start) { if ((uint64_t)offset == dev->part_start || @@ -633,12 +626,12 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) /* Load next part, and start again */ Dmsg0(100, "lseek open next part\n"); if (dvd_open_next_part(dcr) < 0) { - Dmsg0(400, "lseek_dev failed while trying to open the next part\n"); + Dmsg0(400, "lseek_dvd failed while trying to open the next part\n"); return -1; } Dmsg2(100, "Recurse lseek after open next part=%d num_part=%d\n", dev->part, dev->num_dvd_parts); - return lseek_dev(dev, offset, SEEK_SET); + return lseek_dvd(dcr, offset, SEEK_SET); } } else { /* @@ -648,32 +641,32 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) * until the right one is loaded */ Dmsg0(100, "lseek open first part\n"); - if (dvd_open_first_part(dcr, dev->openmode) < 0) { - Dmsg0(400, "lseek_dev failed while trying to open the first part\n"); + if (!dvd_open_first_part(dcr, dev->openmode)) { + Dmsg0(400, "lseek_dvd failed while trying to open the first part\n"); return -1; } Dmsg2(100, "Recurse lseek after open first part=%d num_part=%d\n", dev->part, dev->num_dvd_parts); - return lseek_dev(dev, offset, SEEK_SET); + return lseek_dvd(dcr, offset, SEEK_SET); /* system lseek */ } break; case SEEK_CUR: - Dmsg1(400, "lseek_dev SEEK_CUR to %s\n", edit_int64(offset, ed1)); + Dmsg1(400, "lseek_dvd SEEK_CUR to %s\n", edit_int64(offset, ed1)); if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) { Dmsg0(400, "Seek error.\n"); return pos; } pos += dev->part_start; if (offset == 0) { - Dmsg1(400, "lseek_dev SEEK_CUR returns %s\n", edit_uint64(pos, ed1)); + Dmsg1(400, "lseek_dvd SEEK_CUR returns %s\n", edit_uint64(pos, ed1)); return pos; } else { - Dmsg1(400, "do lseek_dev SEEK_SET %s\n", edit_uint64(pos, ed1)); - return lseek_dev(dev, pos, SEEK_SET); + Dmsg1(400, "do lseek_dvd SEEK_SET %s\n", edit_uint64(pos, ed1)); + return lseek_dvd(dcr, pos, SEEK_SET); } break; case SEEK_END: - Dmsg1(400, "lseek_dev SEEK_END to %s\n", edit_int64(offset, ed1)); + Dmsg1(400, "lseek_dvd SEEK_END to %s\n", edit_int64(offset, ed1)); /* * Bacula does not use offsets for SEEK_END * Also, Bacula uses seek_end only when it wants to @@ -682,7 +675,7 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) * itself is read-only (as currently implemented). */ if (offset > 0) { /* Not used by bacula */ - Dmsg1(400, "lseek_dev SEEK_END called with an invalid offset %s\n", + Dmsg1(400, "lseek_dvd SEEK_END called with an invalid offset %s\n", edit_uint64(offset, ed1)); errno = EINVAL; return -1; @@ -694,7 +687,7 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) { return pos; } else { - Dmsg1(400, "lseek_dev SEEK_END returns %s\n", + Dmsg1(400, "lseek_dvd SEEK_END returns %s\n", edit_uint64(pos + dev->part_start, ed1)); return pos + dev->part_start; } @@ -707,24 +700,24 @@ off_t lseek_dev(DEVICE *dev, off_t offset, int whence) * (useful for DVDs) */ int modesave = dev->openmode; - if (dvd_open_first_part(dcr, OPEN_READ_ONLY) < 0) { - Dmsg0(400, "lseek_dev failed while trying to open the first part\n"); + if (!dvd_open_first_part(dcr, OPEN_READ_ONLY)) { + Dmsg0(400, "lseek_dvd failed while trying to open the first part\n"); return -1; } if (dev->num_dvd_parts > 0) { while (dev->part < dev->num_dvd_parts) { if (dvd_open_next_part(dcr) < 0) { - Dmsg0(400, "lseek_dev failed while trying to open the next part\n"); + Dmsg0(400, "lseek_dvd failed while trying to open the next part\n"); return -1; } } dev->openmode = modesave; if (dvd_open_next_part(dcr) < 0) { - Dmsg0(400, "lseek_dev failed while trying to open the next part\n"); + Dmsg0(400, "lseek_dvd failed while trying to open the next part\n"); return -1; } } - return lseek_dev(dev, 0, SEEK_END); + return lseek_dvd(dcr, 0, SEEK_END); } break; default: @@ -740,7 +733,8 @@ bool dvd_close_job(DCR *dcr) JCR *jcr = dcr->jcr; bool ok = true; - /* If the device is a dvd and WritePartAfterJob + /* + * If the device is a dvd and WritePartAfterJob * is set to yes, open the next part, so, in case of a device * that requires mount, it will be written to the device. */ @@ -764,19 +758,61 @@ bool dvd_close_job(DCR *dcr) return ok; } +void dvd_remove_empty_part(DCR *dcr) +{ + DEVICE *dev = dcr->dev; + + /* Remove the last part file if it is empty */ + if (dev->is_dvd() && dev->num_dvd_parts > 0) { + struct stat statp; + uint32_t part_save = dev->part; + POOL_MEM archive_name(PM_FNAME); + int status; + + dev->part = dev->num_dvd_parts; + make_spooled_dvd_filename(dev, archive_name); + /* Check that the part file is empty */ + 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_save, dev->num_dvd_parts, dev->VolCatInfo.VolCatName); + Dmsg1(100, "unlink(%s)\n", archive_name.c_str()); + unlink(archive_name.c_str()); + if (part_save == dev->part) { + dev->set_part_spooled(false); /* no spooled part left */ + } + } else if (status < 0) { + if (part_save == dev->part) { + dev->set_part_spooled(false); /* spool doesn't exit */ + } + } + dev->part = part_save; /* restore part number */ + } +} + bool truncate_dvd(DCR *dcr) { DEVICE* dev = dcr->dev; - dev->close(); - memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); + dev->clear_freespace_ok(); /* need to update freespace */ + dev->close_part(dcr); if (!unmount_dvd(dev, 1)) { Dmsg0(400, "truncate_dvd: Failed to unmount DVD\n"); return false; } + /* If necessary, delete its spool file. */ + if (dev->is_part_spooled()) { + POOL_MEM archive_name(PM_FNAME); + /* Delete spool file */ + make_spooled_dvd_filename(dev, archive_name); + unlink(archive_name.c_str()); + dev->set_part_spooled(false); + } + /* Set num_dvd_parts to zero (on disk) */ + dev->part = 0; dev->num_dvd_parts = 0; dcr->VolCatInfo.VolCatParts = 0; dev->VolCatInfo.VolCatParts = 0; @@ -784,32 +820,20 @@ bool truncate_dvd(DCR *dcr) Dmsg0(400, "truncate_dvd: Opening first part (1)...\n"); dev->truncating = true; - if (dvd_open_first_part(dcr, CREATE_READ_WRITE) < 0) { + /* This creates a zero length spool file and sets part=1 */ + if (!dvd_open_first_part(dcr, CREATE_READ_WRITE)) { Dmsg0(400, "truncate_dvd: Error while opening first part (1).\n"); dev->truncating = false; return false; } - Dmsg0(400, "truncate_dvd: Truncating...\n"); - - /* If necessary, truncate it spool file. */ - if (ftruncate(dev->fd, 0) != 0) { - berrno be; - Mmsg2(dev->errmsg, _("Unable to truncate device %s. ERR=%s\n"), - dev->print_name(), be.strerror()); - dev->truncating = false; - return false; - } - - dev->close(); - memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo)); + dev->close_part(dcr); Dmsg0(400, "truncate_dvd: Opening first part (2)...\n"); /* - * Now actually truncate the DVD - * This is really kludgy, why not an argument or a separate - * subroutine? KES + * Now actually truncate the DVD which is done by writing + * a zero length part to the DVD/ */ if (!dvd_write_part(dcr)) { Dmsg0(400, "truncate_dvd: Error while writing to DVD.\n"); @@ -819,32 +843,30 @@ bool truncate_dvd(DCR *dcr) dev->truncating = false; /* Set num_dvd_parts to zero (on disk) */ + dev->part = 0; dev->num_dvd_parts = 0; dcr->VolCatInfo.VolCatParts = 0; dev->VolCatInfo.VolCatParts = 0; + /* Clear the size of the volume */ + dev->VolCatInfo.VolCatBytes = 0; + dcr->VolCatInfo.VolCatBytes = 0; /* Update catalog */ if (!dir_update_volume_info(dcr, false)) { return false; } - if (dvd_open_first_part(dcr, OPEN_READ_WRITE) < 0) { - Dmsg0(400, "truncate_dvd: 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). +/* + * 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). * * Note! Normally if we can mount the device, which should be the case * when we get here, it is not a blank DVD. Hence we check if - * there is a zero length file with the right name, in which case - * we allow it. - * This seems terribly kludgie to me. KES + * if all files are of zero length (i.e. no data), in which case we allow it. + * */ bool check_can_write_on_non_blank_dvd(DCR *dcr) { @@ -852,9 +874,8 @@ bool check_can_write_on_non_blank_dvd(DCR *dcr) DIR* dp; struct dirent *entry, *result; int name_max; - int count = 0; - bool matched = false; struct stat filestat; + bool ok = true; name_max = pathconf(".", _PC_NAME_MAX); if (name_max < 1024) { @@ -870,7 +891,7 @@ bool check_can_write_on_non_blank_dvd(DCR *dcr) } entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); - while (1) { + for ( ;; ) { if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { dev->dev_errno = EIO; Dmsg2(129, "check_can_write_on_non_blank_dvd: no more files in dir %s (dev=%s)\n", @@ -879,37 +900,108 @@ bool check_can_write_on_non_blank_dvd(DCR *dcr) } 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 */ + if (strcmp(result->d_name, ".") && strcmp(result->d_name, "..") && + strcmp(result->d_name, ".keep")) { + /* Found a 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); + pm_strcat(filename, result->d_name); 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; + ok = false; + break; } - Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %d\n", + Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %lld\n", filename.c_str(), filestat.st_size); - matched = filestat.st_size == 0; + if (filestat.st_size != 0) { + ok = false; + break; + } } } - count++; } free(entry); closedir(dp); - if (count > 3) { - /* There are more than 3 files (., .., and the volume file) */ - Dmsg1(29, "Cannot write on blank DVD too many files %d greater than 3\n", count); - return false; + Dmsg1(29, "OK can_write_on_non_blank_dvd: OK=%d\n", ok); + return ok; +} + +/* + * Mount a DVD device, then scan to find out how many parts + * there are. + */ +int find_num_dvd_parts(DCR *dcr) +{ + DEVICE *dev = dcr->dev; + int num_parts = 0; + + if (!dev->is_dvd()) { + return 0; } - Dmsg2(29, "OK can_write_on_non_blank_dvd: got %d files in the mount point (matched=%d)\n", count, matched); - return matched; + if (dev->mount(1)) { + DIR* dp; + struct dirent *entry, *result; + int name_max; + int len = strlen(dcr->VolCatInfo.VolCatName); + + /* Now count the number of parts */ + 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, "find_num_dvd_parts: failed to open dir %s (dev=%s), ERR=%s\n", + dev->device->mount_point, dev->print_name(), be.strerror()); + goto get_out; + } + + entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); + + Dmsg1(100, "Looking for Vol=%s\n", dcr->VolCatInfo.VolCatName); + for ( ;; ) { + int flen; + bool ignore; + if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { + dev->dev_errno = EIO; + Dmsg2(129, "find_num_dvd_parts: failed to find suitable file in dir %s (dev=%s)\n", + dev->device->mount_point, dev->print_name()); + break; + } + flen = strlen(result->d_name); + ignore = true; + if (flen >= len) { + result->d_name[len] = 0; + if (strcmp(dcr->VolCatInfo.VolCatName, result->d_name) == 0) { + num_parts++; + Dmsg1(100, "find_num_dvd_parts: found part: %s\n", result->d_name); + ignore = false; + } + } + if (ignore) { + Dmsg2(129, "find_num_dvd_parts: ignoring %s in %s\n", + result->d_name, dev->device->mount_point); + } + } + free(entry); + closedir(dp); + Dmsg1(29, "find_num_dvd_parts = %d\n", num_parts); + } + +get_out: + dev->set_freespace_ok(); + if (dev->is_mounted()) { + dev->unmount(0); + } + return num_parts; }