+ POOL_MEM archive_name(PM_FNAME);
+
+ get_autochanger_loaded_slot(dcr);
+
+ /*
+ * Handle opening of File Archive (not a tape)
+ */
+
+ pm_strcpy(archive_name, dev_name);
+ /*
+ * If this is a virtual autochanger (i.e. changer_res != NULL)
+ * we simply use the device name, assuming it has been
+ * appropriately setup by the "autochanger".
+ */
+ if (!device->changer_res) {
+ if (VolCatInfo.VolCatName[0] == 0) {
+ Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"),
+ print_name());
+ clear_opened();
+ return;
+ }
+
+ if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
+ pm_strcat(archive_name, "/");
+ }
+ pm_strcat(archive_name, VolCatInfo.VolCatName);
+ }
+
+ mount(1); /* do mount if required */
+
+ openmode = omode;
+ set_mode(omode);
+ /* If creating file, give 0640 permissions */
+ Dmsg3(29, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode),
+ archive_name.c_str(), mode);
+ /* 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());
+ Dmsg1(29, "open failed: %s", errmsg);
+ Emsg0(M_FATAL, 0, errmsg);
+ } else {
+ dev_errno = 0;
+ file = 0;
+ file_addr = 0;
+ }
+ Dmsg4(29, "open dev: disk fd=%d opened, part=%d/%d, part_size=%u\n",
+ fd, part, num_dvd_parts, part_size);
+}
+
+/*
+ * 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.
+ */
+void DEVICE::open_dvd_device(DCR *dcr, int omode)
+{
+ POOL_MEM archive_name(PM_FNAME);
+ struct stat filestat;
+
+ /*
+ * Handle opening of DVD Volume
+ */
+ Dmsg2(29, "Enter: open_dvd_dev: DVD vol=%s mode=%s\n",
+ &dcr->VolCatInfo, mode_to_str(omode));
+
+ /*
+ * For a DVD we must always pull the state info from dcr->VolCatInfo
+ * This is a bit ugly, but is necessary because we need to open/close/re-open
+ * the dvd file in order to properly mount/unmount and access the
+ * DVD. So we store the state of the DVD as far as is known in the
+ * catalog in dcr->VolCatInfo, and thus we refresh the dev->VolCatInfo
+ * copy here, when opening.
+ */
+ VolCatInfo = dcr->VolCatInfo; /* structure assignment */
+ Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName);
+
+ if (VolCatInfo.VolCatName[0] == 0) {
+ Dmsg1(10, "Could not open DVD device %s. No Volume name given.\n",
+ print_name());
+ Mmsg(errmsg, _("Could not open DVD device %s. No Volume name given.\n"),
+ print_name());
+ clear_opened();
+ return;
+ }
+
+ if (part == 0) {
+ Dmsg0(100, "Set part=1\n");
+ part = 1; /* count from 1 */
+ file_size = 0;
+ }
+ part_size = 0;
+ if (num_dvd_parts != VolCatInfo.VolCatParts) {
+ num_dvd_parts = VolCatInfo.VolCatParts;
+ }
+
+ /*
+ * If we are not trying to access the last part, set mode to
+ * OPEN_READ_ONLY as writing would be an error.
+ */
+ Dmsg2(29, "open DVD part=%d num_dvd_parts=%d\n", part, num_dvd_parts);
+ /* Now find the name of the part that we want to access */
+ if (part <= num_dvd_parts) {
+ omode = OPEN_READ_ONLY;
+ make_mounted_dvd_filename(this, archive_name);
+ set_part_spooled(false);
+ } else {
+ omode = OPEN_READ_WRITE;
+ make_spooled_dvd_filename(this, archive_name);
+ set_part_spooled(true);
+ }
+ set_mode(omode);
+
+ // Clear any previous blank_dvd status - we will recalculate it here
+ blank_dvd = false;
+
+ Dmsg3(99, "open_dvd_device: part=%d num_dvd_parts=%d, VolCatInfo.VolCatParts=%d\n",
+ part, num_dvd_parts, dcr->VolCatInfo.VolCatParts);
+
+ if (mount(1)) {
+ Dmsg0(99, "DVD device mounted.\n");
+ if (num_dvd_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 to truncate a volume).
+ */
+ if (!check_can_write_on_non_blank_dvd(dcr)) {
+ Mmsg(errmsg, _("The DVD in device %s contains data, please blank it before writing.\n"), print_name());
+ Emsg0(M_FATAL, 0, errmsg);
+ unmount(1); /* Unmount the device, so the operator can change it. */
+ clear_opened();
+ return;
+ }
+ blank_dvd = true;
+ } else {
+ /*
+ * Ensure that we have the correct DVD loaded by looking for part1.
+ * We only succeed the open if it exists. Failure to do this could
+ * leave us trying to add a part to a different DVD!
+ */
+ uint32_t oldpart = part;
+ struct stat statp;
+ POOL_MEM part1_name(PM_FNAME);
+ part = 1;
+ make_mounted_dvd_filename(this, part1_name);
+ part = oldpart;
+ if (stat(part1_name.c_str(), &statp) < 0) {
+ berrno be;
+ Mmsg(errmsg, _("Unable to stat DVD part 1 file %s: ERR=%s\n"),
+ part1_name.c_str(), be.strerror());
+ Emsg0(M_FATAL, 0, errmsg);
+ clear_opened();
+ return;
+ }
+ if (!S_ISREG(statp.st_mode)) {
+ /* It is not a regular file */
+ Mmsg(errmsg, _("DVD part 1 is not a regular file %s.\n"),
+ part1_name.c_str());
+ Emsg0(M_FATAL, 0, errmsg);
+ clear_opened();
+ return;
+ }
+ }
+ } else {
+ Dmsg0(99, "DVD device mount failed.\n");
+ /* We cannot mount the device */
+ if (num_dvd_parts == 0) {
+ /* Run free space, check there is a media. */
+ if (!update_freespace()) {
+ Emsg0(M_FATAL, 0, errmsg);
+ clear_opened();
+ return;
+ }
+ if (have_media()) {
+ Dmsg1(29, "Could not mount device %s, this is not a problem (num_dvd_parts == 0), and have media.\n", print_name());
+ } else {
+ Mmsg(errmsg, _("There is no valid DVD in device %s.\n"), print_name());
+ Emsg0(M_FATAL, 0, errmsg);
+ clear_opened();
+ return;
+ }
+ } else {
+ Mmsg(errmsg, _("Could not mount DVD device %s.\n"), print_name());
+ Emsg0(M_FATAL, 0, errmsg);
+ clear_opened();
+ return;
+ }
+ }
+
+ Dmsg5(29, "open dev: DVD dev=%s mode=%s part=%d npart=%d volcatnparts=%d\n",
+ archive_name.c_str(), mode_to_str(omode),
+ part, num_dvd_parts, dcr->VolCatInfo.VolCatParts);
+ openmode = omode;
+ Dmsg2(100, "openmode=%d %s\n", openmode, mode_to_str(openmode));
+
+
+ /* If creating file, give 0640 permissions */
+ Dmsg3(29, "mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode),
+ archive_name.c_str(), mode);
+ /* Use system open() */
+ if ((fd = ::open(archive_name.c_str(), mode, 0640)) < 0) {
+ berrno be;
+ Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(),
+ be.strerror());
+ // Should this be set if we try the create/open below
+ dev_errno = EIO; /* Interpreted as no device present by acquire.c:acquire_device_for_read(). */
+ Dmsg1(29, "open failed: %s", errmsg);
+
+ /* Previous open failed. See if we can recover */
+ if ((omode == OPEN_READ_ONLY || omode == OPEN_READ_WRITE) &&
+ (part > num_dvd_parts)) {
+ /* If the last part (on spool), doesn't exist when accessing,
+ * create it. In read/write mode a write will be allowed (higher
+ * level software thinks that we are extending a pre-existing
+ * media. Reads for READ_ONLY will report immediately an EOF
+ * Sometimes it is better to finish with an EOF than with an error. */
+ Dmsg1(29, "Creating last part on spool: %s\n", archive_name.c_str());
+ omode = CREATE_READ_WRITE;
+ set_mode(CREATE_READ_WRITE);
+ fd = ::open(archive_name.c_str(), mode, 0640);
+ set_mode(omode);
+ }
+ }
+ Dmsg1(100, "after open fd=%d\n", fd);
+ if (is_open()) {
+ if (omode == OPEN_READ_WRITE || omode == CREATE_READ_WRITE) {
+ set_append();
+ }
+ /* Get size of file */
+ if (fstat(fd, &filestat) < 0) {
+ berrno be;
+ dev_errno = errno;
+ Mmsg2(errmsg, _("Could not fstat: %s, ERR=%s\n"), archive_name.c_str(),
+ be.strerror());
+ Dmsg1(29, "open failed: %s", errmsg);
+ /* Use system close() */
+ ::close(fd);
+ clear_opened();
+ } else {
+ part_size = filestat.st_size;
+ dev_errno = 0;
+ update_pos(dcr); /* update position */
+ }
+ }