+ return true;
+}
+
+static void reread_last_block(DCR *dcr)
+{
+#define CHECK_LAST_BLOCK
+#ifdef CHECK_LAST_BLOCK
+ bool ok = true;
+ DEVICE *dev = dcr->dev;
+ JCR *jcr = dcr->jcr;
+ DEV_BLOCK *block = dcr->block;
+ /*
+ * If the device is a tape and it supports backspace record,
+ * we backspace over one or two eof marks depending on
+ * how many we just wrote, then over the last record,
+ * then re-read it and verify that the block number is
+ * correct.
+ */
+ if (dev->is_tape() && dev->has_cap(CAP_BSR)) {
+ /* Now back up over what we wrote and read the last block */
+ if (!dev->bsf(1)) {
+ berrno be;
+ ok = false;
+ Jmsg(jcr, M_ERROR, 0, _("Backspace file at EOT failed. ERR=%s\n"),
+ be.strerror(dev->dev_errno));
+ }
+ if (ok && dev->has_cap(CAP_TWOEOF) && !dev->bsf(1)) {
+ berrno be;
+ ok = false;
+ Jmsg(jcr, M_ERROR, 0, _("Backspace file at EOT failed. ERR=%s\n"),
+ be.strerror(dev->dev_errno));
+ }
+ /* Backspace over record */
+ if (ok && !dev->bsr(1)) {
+ berrno be;
+ ok = false;
+ Jmsg(jcr, M_ERROR, 0, _("Backspace record at EOT failed. ERR=%s\n"),
+ be.strerror(dev->dev_errno));
+ /*
+ * On FreeBSD systems, if the user got here, it is likely that his/her
+ * tape drive is "frozen". The correct thing to do is a
+ * rewind(), but if we do that, higher levels in cleaning up, will
+ * most likely write the EOS record over the beginning of the
+ * tape. The rewind *is* done later in mount.c when another
+ * tape is requested. Note, the clrerror() call in bsr()
+ * calls ioctl(MTCERRSTAT), which *should* fix the problem.
+ */
+ }
+ if (ok) {
+ DEV_BLOCK *lblock = new_block(dev);
+ /* Note, this can destroy dev->errmsg */
+ dcr->block = lblock;
+ if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) {
+ Jmsg(jcr, M_ERROR, 0, _("Re-read last block at EOT failed. ERR=%s"),
+ dev->errmsg);
+ } else {
+ /*
+ * If we wrote block and the block numbers don't agree
+ * we have a possible problem.
+ */
+ if (lblock->VolSessionId == block->VolSessionId &&
+ lblock->BlockNumber+1 != block->BlockNumber) {
+ Jmsg(jcr, M_ERROR, 0, _(
+"Re-read of last block OK, but block numbers differ. Last block=%u Current block=%u.\n"),
+ lblock->BlockNumber, block->BlockNumber);
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("Re-read of last block succeeded.\n"));
+ }
+ }
+ free_block(lblock);
+ dcr->block = block;
+ }
+ }
+#endif
+}
+
+static bool terminate_writing_volume(DCR *dcr)
+{
+ DEVICE *dev = dcr->dev;
+ bool ok = true;
+
+ /* Create a JobMedia record to indicated end of tape */
+ dev->VolCatInfo.VolCatFiles = dev->file;
+ if (!dir_create_jobmedia_record(dcr)) {
+ Dmsg0(190, "Error from create JobMedia\n");
+ dev->dev_errno = EIO;
+ Jmsg(dcr->jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
+ dcr->VolCatInfo.VolCatName, dcr->jcr->Job);
+ ok = false;
+ goto bail_out;
+ }
+ dcr->block->write_failed = true;
+ if (!dev->weof(1)) { /* end the tape */
+ dev->VolCatInfo.VolCatErrors++;
+ Jmsg(dcr->jcr, M_ERROR, 0, _("Error writing final EOF to tape. This Volume may not be readable.\n"
+ "%s"), dev->errmsg);
+ ok = false;
+ Dmsg0(100, "WEOF error.\n");
+ }
+ if (ok) {
+ ok = write_ansi_ibm_labels(dcr, ANSI_EOV_LABEL, dev->VolHdr.VolumeName);
+ }
+ bstrncpy(dev->VolCatInfo.VolCatStatus, "Full", sizeof(dev->VolCatInfo.VolCatStatus));
+ dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */
+
+ if (dev->is_dvd()) {
+ if (!dvd_write_part(dcr)) { /* write last part */
+ dev->VolCatInfo.VolCatErrors++;
+ Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing final part to DVD. "
+ "This Volume may not be readable.\n%s"),
+ dev->errmsg);
+ ok = false;
+ Dmsg0(100, "dvd_write_part error.\n");
+ }
+ dev->VolCatInfo.VolCatParts = dev->num_dvd_parts;
+ }
+
+ if (!dir_update_volume_info(dcr, false)) {
+ ok = false;
+ }
+ Dmsg1(100, "dir_update_volume_info terminate writing -- %s\n", ok?"OK":"ERROR");
+
+ /*
+ * Walk through all attached dcrs setting flag to call
+ * set_new_file_parameters() when that dcr is next used.
+ */
+ DCR *mdcr;
+ foreach_dlist(mdcr, dev->attached_dcrs) {
+ if (mdcr->jcr->JobId == 0) {
+ continue;
+ }
+ mdcr->NewFile = true; /* set reminder to do set_new_file_params */
+ }
+ /* Set new file/block parameters for current dcr */
+ set_new_file_parameters(dcr);
+
+ if (ok && dev->has_cap(CAP_TWOEOF) && !dev->weof(1)) { /* end the tape */
+ dev->VolCatInfo.VolCatErrors++;
+ /* This may not be fatal since we already wrote an EOF */
+ Jmsg(dcr->jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
+bail_out:
+ dev->set_ateot(); /* no more writing this tape */
+ Dmsg1(100, "Leave terminate_writing_volume -- %s\n", ok?"OK":"ERROR");
+ return ok;
+}
+
+/*
+ * Do bookkeeping when a new file is created on a Volume. This is
+ * also done for disk files to generate the jobmedia records for
+ * quick seeking.
+ */
+static bool do_new_file_bookkeeping(DCR *dcr)
+{
+ DEVICE *dev = dcr->dev;
+ JCR *jcr = dcr->jcr;
+
+ /* Create a JobMedia record so restore can seek */
+ if (!dir_create_jobmedia_record(dcr)) {
+ Dmsg0(190, "Error from create_job_media.\n");
+ dev->dev_errno = EIO;
+ Jmsg(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
+ dcr->VolCatInfo.VolCatName, jcr->Job);
+ terminate_writing_volume(dcr);
+ dev->dev_errno = EIO;
+ return false;
+ }
+ dev->VolCatInfo.VolCatFiles = dev->file;
+ if (!dir_update_volume_info(dcr, false)) {
+ Dmsg0(190, "Error from update_vol_info.\n");
+ terminate_writing_volume(dcr);
+ dev->dev_errno = EIO;
+ return false;
+ }
+ Dmsg0(100, "dir_update_volume_info max file size -- OK\n");
+
+ /*
+ * Walk through all attached dcrs setting flag to call
+ * set_new_file_parameters() when that dcr is next used.
+ */
+ DCR *mdcr;
+ foreach_dlist(mdcr, dev->attached_dcrs) {
+ if (mdcr->jcr->JobId == 0) {
+ continue;
+ }
+ mdcr->NewFile = true; /* set reminder to do set_new_file_params */
+ }
+ /* Set new file/block parameters for current dcr */
+ set_new_file_parameters(dcr);
+ return true;
+}
+
+/*
+ * Do all checks for DVD sizes during writing.
+ */
+static bool do_dvd_size_checks(DCR *dcr)
+{
+ DEVICE *dev = dcr->dev;
+ JCR *jcr = dcr->jcr;
+ DEV_BLOCK *block = dcr->block;
+
+ /* Don't go further if the device is not a dvd */
+ if (!dev->is_dvd()) {
+ return true;
+ }
+
+ /* Limit maximum part size to value specified by user
+ */
+ if (dev->max_part_size > 0 && ((dev->part_size + block->binbuf) >= dev->max_part_size)) {
+ if (dev->part < dev->num_dvd_parts) {
+ Jmsg3(dcr->jcr, M_FATAL, 0, _("Error while writing, current part number"
+ " is less than the total number of parts (%d/%d, device=%s)\n"),
+ dev->part, dev->num_dvd_parts, dev->print_name());
+ dev->dev_errno = EIO;
+ return false;
+ }
+
+ if (dvd_open_next_part(dcr) < 0) {
+ Jmsg2(dcr->jcr, M_FATAL, 0, _("Unable to open device next part %s: ERR=%s\n"),
+ dev->print_name(), dev->bstrerror());
+ dev->dev_errno = EIO;
+ return false;
+ }
+
+ dev->VolCatInfo.VolCatParts = dev->num_dvd_parts;
+
+ if (!dir_update_volume_info(dcr, false)) {
+ Dmsg0(190, "Error from update_vol_info.\n");
+ dev->dev_errno = EIO;
+ return false;
+ }
+ }
+
+ dev->update_freespace();
+
+ if (!dev->is_freespace_ok()) { /* Error while getting free space */
+ char ed1[50], ed2[50];
+ Dmsg1(10, "Cannot get free space on the device ERR=%s.\n", dev->errmsg);
+ Jmsg(jcr, M_FATAL, 0, _("End of Volume \"%s\" at %u:%u on device %s "
+ "(part_size=%s, free_space=%s, free_space_errno=%d, errmsg=%s).\n"),
+ dev->VolCatInfo.VolCatName,
+ dev->file, dev->block_num, dev->print_name(),
+ edit_uint64_with_commas(dev->part_size, ed1), edit_uint64_with_commas(dev->free_space, ed2),
+ dev->free_space_errno, dev->errmsg);
+ dev->dev_errno = dev->free_space_errno;
+ return false;
+ }
+
+ if ((dev->is_freespace_ok() && (dev->part_size + block->binbuf) >= dev->free_space)) {
+ char ed1[50], ed2[50];
+ Dmsg0(10, "==== Just enough free space on the device to write the current part...\n");
+ Jmsg(jcr, M_INFO, 0, _("End of Volume \"%s\" at %u:%u on device %s "
+ "(part_size=%s, free_space=%s, free_space_errno=%d).\n"),
+ dev->VolCatInfo.VolCatName,
+ dev->file, dev->block_num, dev->print_name(),
+ edit_uint64_with_commas(dev->part_size, ed1), edit_uint64_with_commas(dev->free_space, ed2),
+ dev->free_space_errno);
+ terminate_writing_volume(dcr);
+ dev->dev_errno = ENOSPC;
+ return false;
+ }
+ return true;