/* * Reposition the device to file, block * * Returns: false on failure * true on success */ bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock) { if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to reposition. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } if (is_fifo() || is_vtl()) { return true; } boffset_t pos = (((boffset_t)rfile) << 32) | rblock; Dmsg1(100, "===== lseek to %d\n", (int)pos); if (lseek(dcr, pos, SEEK_SET) == (boffset_t)-1) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), print_name(), be.bstrerror()); return false; } file = rfile; block_num = rblock; file_addr = pos; return true; }
/* * Set the position of the device -- only for files and DVD * For other devices, there is no generic way to do it. * Returns: true on succes * false on error */ bool DEVICE::update_pos(DCR *dcr) { boffset_t pos; bool ok = true; if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad device call. Device not open\n")); Emsg1(M_FATAL, 0, "%s", errmsg); return false; } if (is_file()) { file = 0; file_addr = 0; pos = lseek(dcr, (boffset_t)0, SEEK_CUR); if (pos < 0) { berrno be; dev_errno = errno; Pmsg1(000, _("Seek error: ERR=%s\n"), be.bstrerror()); Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), print_name(), be.bstrerror()); ok = false; } else { file_addr = pos; block_num = (uint32_t)pos; file = (uint32_t)(pos >> 32); } } return ok; }
/* * Load medium in device * * Returns: true on success * false on failure */ bool generic_tape_device::load_dev() { #ifdef MTLOAD struct mtop mt_com; #endif if (m_fd < 0) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to load_dev. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } #ifndef MTLOAD Dmsg0(200, "stored: MTLOAD command not available\n"); berrno be; dev_errno = ENOTTY; /* function not available */ Mmsg2(errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); return false; #else block_num = file = 0; file_size = 0; file_addr = 0; mt_com.mt_op = MTLOAD; mt_com.mt_count = 1; if (d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); return false; } return true; #endif }
/* * Backward space a record * * Returns: false on failure * true on success */ bool generic_tape_device::bsr(int num) { struct mtop mt_com; int status; if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to bsr_dev. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } if (!has_cap(CAP_BSR)) { Mmsg1(errmsg, _("ioctl MTBSR not permitted on %s.\n"), prt_name); return false; } Dmsg0(100, "bsr_dev\n"); block_num -= num; clear_eof(); clear_eot(); mt_com.mt_op = MTBSR; mt_com.mt_count = num; status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status < 0) { berrno be; clrerror(mt_com.mt_op); Mmsg2(errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); } return status == 0; }
/* * Backward space a file * * Returns: false on failure * true on success */ bool generic_tape_device::bsf(int num) { struct mtop mt_com; int status; if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to bsf. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } Dmsg0(100, "bsf\n"); clear_eot(); clear_eof(); file -= num; file_addr = 0; file_size = 0; mt_com.mt_op = MTBSF; mt_com.mt_count = num; status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status < 0) { berrno be; clrerror(mt_com.mt_op); Mmsg2(errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); } return status == 0; }
/* * Write an end of file on the device * * Returns: true on success * false on failure */ bool generic_tape_device::weof(int num) { struct mtop mt_com; int status; Dmsg1(129, "=== weof_dev=%s\n", prt_name); if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to weof_dev. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } file_size = 0; if (!can_append()) { Mmsg0(errmsg, _("Attempt to WEOF on non-appendable Volume\n")); Emsg0(M_FATAL, 0, errmsg); return false; } clear_eof(); clear_eot(); mt_com.mt_op = MTWEOF; mt_com.mt_count = num; status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status == 0) { block_num = 0; file += num; file_addr = 0; } else { berrno be; clrerror(mt_com.mt_op); if (status == -1) { Mmsg2(errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); } } return status == 0; }
/* * Open a new connexion to mdb catalog. This function is used * by batch and accurate mode. */ bool db_open_batch_connection(JCR *jcr, B_DB *mdb) { bool multi_db; multi_db = mdb->batch_insert_available(); if (!jcr->db_batch) { jcr->db_batch = db_clone_database_connection(mdb, jcr, multi_db, multi_db); if (!jcr->db_batch) { Mmsg0(&mdb->errmsg, _("Could not init database batch connection\n")); Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg); return false; } } return true; }
/* * Foward space num records * * Returns: false on failure * true on success */ bool generic_tape_device::fsr(int num) { struct mtop mt_com; int status; if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to fsr. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } if (!has_cap(CAP_FSR)) { Mmsg1(errmsg, _("ioctl MTFSR not permitted on %s.\n"), prt_name); return false; } Dmsg1(100, "fsr %d\n", num); mt_com.mt_op = MTFSR; mt_com.mt_count = num; status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status == 0) { clear_eof(); block_num += num; } else { berrno be; struct mtget mt_stat; clrerror(mt_com.mt_op); Dmsg1(100, "FSF fail: ERR=%s\n", be.bstrerror()); if (dev_get_os_pos(this, &mt_stat)) { Dmsg4(100, "Adjust from %d:%d to %d:%d\n", file, block_num, mt_stat.mt_fileno, mt_stat.mt_blkno); file = mt_stat.mt_fileno; block_num = mt_stat.mt_blkno; } else { if (at_eof()) { set_eot(); } else { set_ateof(); } } Mmsg3(errmsg, _("ioctl MTFSR %d error on %s. ERR=%s.\n"), num, prt_name, be.bstrerror()); } return status == 0; }
/** * Request to mount specific Volume * * Entered with device blocked and dcr->VolumeName is desired * volume. * Leaves with device blocked. * * Returns: true on success (operator issues a mount command) * false on failure * Note, must create dev->errmsg on error return. * */ bool dir_ask_sysop_to_mount_volume(DCR *dcr, int mode) { int status = W_TIMEOUT; DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; Dmsg0(dbglvl, "enter dir_ask_sysop_to_mount_volume\n"); if (!dcr->VolumeName[0]) { Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n")); return false; } ASSERT(dev->blocked()); for ( ;; ) { if (job_canceled(jcr)) { Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"), jcr->Job, dev->print_name()); return false; } /* * If we are not polling, and the wait timeout or the * user explicitly did a mount, send him the message. * Otherwise skip it. */ if (!dev->poll && (status == W_TIMEOUT || status == W_MOUNT)) { const char *msg; if (mode == ST_APPENDREADY) { msg = _("Please mount append Volume \"%s\" or label a new one for:\n" " Job: %s\n" " Storage: %s\n" " Pool: %s\n" " Media type: %s\n"); } else { msg = _("Please mount read Volume \"%s\" for:\n" " Job: %s\n" " Storage: %s\n" " Pool: %s\n" " Media type: %s\n"); } Jmsg(jcr, M_MOUNT, 0, msg, dcr->VolumeName, jcr->Job, dev->print_name(), dcr->pool_name, dcr->media_type); Dmsg3(dbglvl, "Mount \"%s\" on device \"%s\" for Job %s\n", dcr->VolumeName, dev->print_name(), jcr->Job); } jcr->sendJobStatus(JS_WaitMount); status = wait_for_sysop(dcr); /* wait on device */ Dmsg1(dbglvl, "Back from wait_for_sysop status=%d\n", status); if (dev->poll) { Dmsg1(dbglvl, "Poll timeout in mount vol on device %s\n", dev->print_name()); Dmsg1(dbglvl, "Blocked=%s\n", dev->print_blocked()); goto get_out; } if (status == W_TIMEOUT) { if (!double_dev_wait_time(dev)) { Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"), dev->print_name(), jcr->Job); Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg); Dmsg1(dbglvl, "Gave up waiting on device %s\n", dev->print_name()); return false; /* exceeded maximum waits */ } continue; } if (status == W_ERROR) { berrno be; Mmsg(dev->errmsg, _("pthread error in mount_volume\n")); Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg); return false; } Dmsg1(dbglvl, "Someone woke me for device %s\n", dev->print_name()); break; } get_out: jcr->sendJobStatus(JS_Running); Dmsg0(dbglvl, "leave dir_ask_sysop_to_mount_volume\n"); return true; }
/** * Request the sysop to create an appendable volume * * Entered with device blocked. * Leaves with device blocked. * * Returns: true on success (operator issues a mount command) * false on failure * Note, must create dev->errmsg on error return. * * On success, dcr->VolumeName and dcr->VolCatInfo contain * information on suggested volume, but this may not be the * same as what is actually mounted. * * When we return with success, the correct tape may or may not * actually be mounted. The calling routine must read it and * verify the label. */ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { int status = W_TIMEOUT; DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; bool got_vol = false; if (job_canceled(jcr)) { return false; } Dmsg0(dbglvl, "enter dir_ask_sysop_to_create_appendable_volume\n"); ASSERT(dev->blocked()); for ( ;; ) { if (job_canceled(jcr)) { Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"), jcr->Job, dev->print_name()); Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg); return false; } got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */ if (got_vol) { goto get_out; } else { if (status == W_TIMEOUT || status == W_MOUNT) { Mmsg(dev->errmsg, _( "Job %s is waiting. Cannot find any appendable volumes.\n" "Please use the \"label\" command to create a new Volume for:\n" " Storage: %s\n" " Pool: %s\n" " Media type: %s\n"), jcr->Job, dev->print_name(), dcr->pool_name, dcr->media_type); Jmsg(jcr, M_MOUNT, 0, "%s", dev->errmsg); Dmsg1(dbglvl, "%s", dev->errmsg); } } jcr->sendJobStatus(JS_WaitMedia); status = wait_for_sysop(dcr); Dmsg1(dbglvl, "Back from wait_for_sysop status=%d\n", status); if (dev->poll) { Dmsg1(dbglvl, "Poll timeout in create append vol on device %s\n", dev->print_name()); continue; } if (status == W_TIMEOUT) { if (!double_dev_wait_time(dev)) { Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"), dev->print_name(), jcr->Job); Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg); Dmsg1(dbglvl, "Gave up waiting on device %s\n", dev->print_name()); return false; /* exceeded maximum waits */ } continue; } if (status == W_ERROR) { berrno be; Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n")); Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg); return false; } Dmsg1(dbglvl, "Someone woke me for device %s\n", dev->print_name()); } get_out: jcr->sendJobStatus(JS_Running); Dmsg0(dbglvl, "leave dir_ask_sysop_to_mount_create_appendable_volume\n"); return true; }
/* * We read an ANSI label and compare the Volume name. We require * a VOL1 record of 80 characters followed by a HDR1 record containing * BACULA.DATA in the filename field. We then read up to 3 more * header records (they are not required) and an EOF, at which * point, all is good. * * Returns: * VOL_OK Volume name OK * VOL_NO_LABEL No ANSI label on Volume * VOL_IO_ERROR I/O error on read * VOL_NAME_ERROR Wrong name in VOL1 record * VOL_LABEL_ERROR Probably an ANSI label, but something wrong * */ int read_ansi_ibm_label(DCR *dcr) { DEVICE * volatile dev = dcr->dev; JCR *jcr = dcr->jcr; char label[80]; /* tape label */ int stat, i; char *VolName = dcr->VolumeName; bool ok = false; /* * Read VOL1, HDR1, HDR2 labels, but ignore the data * If tape read the following EOF mark, on disk do * not read. */ Dmsg0(100, "Read ansi label.\n"); if (!dev->is_tape()) { return VOL_OK; } dev->label_type = B_BACULA_LABEL; /* assume Bacula label */ /* Read a maximum of 5 records VOL1, HDR1, ... HDR4 */ for (i=0; i < 6; i++) { do { stat = dev->read(label, sizeof(label)); } while (stat == -1 && errno == EINTR); if (stat < 0) { berrno be; dev->clrerror(-1); Dmsg1(100, "Read device got: ERR=%s\n", be.bstrerror()); Mmsg2(jcr->errmsg, _("Read error on device %s in ANSI label. ERR=%s\n"), dev->dev_name, be.bstrerror()); Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); dev->VolCatInfo.VolCatErrors++; return VOL_IO_ERROR; } if (stat == 0) { if (dev->at_eof()) { dev->set_eot(); /* second eof, set eot bit */ Dmsg0(100, "EOM on ANSI label\n"); Mmsg0(jcr->errmsg, _("Insane! End of tape while reading ANSI label.\n")); return VOL_LABEL_ERROR; /* at EOM this shouldn't happen */ } else { dev->set_ateof(); /* set eof state */ } } switch (i) { case 0: /* Want VOL1 label */ if (stat == 80) { if (strncmp("VOL1", label, 4) == 0) { ok = true; dev->label_type = B_ANSI_LABEL; Dmsg0(100, "Got ANSI VOL1 label\n"); } else { /* Try EBCDIC */ ebcdic_to_ascii(label, label, sizeof(label)); if (strncmp("VOL1", label, 4) == 0) { ok = true;; dev->label_type = B_IBM_LABEL; Dmsg0(100, "Found IBM label.\n"); Dmsg0(100, "Got IBM VOL1 label\n"); } } } if (!ok) { Dmsg0(100, "No VOL1 label\n"); Mmsg0(jcr->errmsg, _("No VOL1 label while reading ANSI/IBM label.\n")); return VOL_NO_LABEL; /* No ANSI label */ } /* Compare Volume Names allow special wild card */ if (VolName && *VolName && *VolName != '*') { if (!same_label_names(VolName, &label[4])) { char *p = &label[4]; char *q; free_volume(dev); /* Store new Volume name */ q = dev->VolHdr.VolumeName; for (int i=0; *p != ' ' && i < 6; i++) { *q++ = *p++; } *q = 0; Dmsg0(100, "Call reserve_volume\n"); /* ***FIXME*** why is this reserve_volume() needed???? KES */ reserve_volume(dcr, dev->VolHdr.VolumeName); dev = dcr->dev; /* may have changed in reserve_volume */ Dmsg2(100, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolumeName); Mmsg2(jcr->errmsg, _("Wanted ANSI Volume \"%s\" got \"%s\"\n"), VolName, dev->VolHdr.VolumeName); return VOL_NAME_ERROR; } } break; case 1: if (dev->label_type == B_IBM_LABEL) { ebcdic_to_ascii(label, label, sizeof(label)); } if (stat != 80 || strncmp("HDR1", label, 4) != 0) { Dmsg0(100, "No HDR1 label\n"); Mmsg0(jcr->errmsg, _("No HDR1 label while reading ANSI label.\n")); return VOL_LABEL_ERROR; } if (strncmp("BACULA.DATA", &label[4], 11) != 0) { Dmsg1(100, "HD1 not Bacula label. Wanted BACULA.DATA got %11s\n", &label[4]); Mmsg1(jcr->errmsg, _("ANSI/IBM Volume \"%s\" does not belong to Bacula.\n"), dev->VolHdr.VolumeName); return VOL_NAME_ERROR; /* Not a Bacula label */ } Dmsg0(100, "Got HDR1 label\n"); break; case 2: if (dev->label_type == B_IBM_LABEL) { ebcdic_to_ascii(label, label, sizeof(label)); } if (stat != 80 || strncmp("HDR2", label, 4) != 0) { Dmsg0(100, "No HDR2 label\n"); Mmsg0(jcr->errmsg, _("No HDR2 label while reading ANSI/IBM label.\n")); return VOL_LABEL_ERROR; } Dmsg0(100, "Got ANSI HDR2 label\n"); break; default: if (stat == 0) { Dmsg0(100, "ANSI label OK\n"); return VOL_OK; } if (dev->label_type == B_IBM_LABEL) { ebcdic_to_ascii(label, label, sizeof(label)); } if (stat != 80 || strncmp("HDR", label, 3) != 0) { Dmsg0(100, "Unknown or bad ANSI/IBM label record.\n"); Mmsg0(jcr->errmsg, _("Unknown or bad ANSI/IBM label record.\n")); return VOL_LABEL_ERROR; } Dmsg0(100, "Got HDR label\n"); break; } } Dmsg0(100, "Too many records in ANSI/IBM label.\n"); Mmsg0(jcr->errmsg, _("Too many records in while reading ANSI/IBM label.\n")); return VOL_LABEL_ERROR; }
/* * Open a volume using libdroplet. */ int object_store_device::d_open(const char *pathname, int flags, int mode) { dpl_status_t status; dpl_vfile_flag_t dpl_flags; dpl_option_t dpl_options; #if 1 Mmsg1(errmsg, _("Object Storage devices are not yet supported, please disable %s\n"), dev_name); return -1; #endif /* * Initialize the droplet library when its not done previously. */ P(mutex); if (droplet_reference_count == 0) { status = dpl_init(); if (status != DPL_SUCCESS) { V(mutex); return -1; } dpl_set_log_func(object_store_logfunc); droplet_reference_count++; } V(mutex); if (!m_object_configstring) { int len; char *bp, *next_option; bool done; if (!dev_options) { Mmsg0(errmsg, _("No device options configured\n")); Emsg0(M_FATAL, 0, errmsg); return -1; } m_object_configstring = bstrdup(dev_options); bp = m_object_configstring; while (bp) { next_option = strchr(bp, ','); if (next_option) { *next_option++ = '\0'; } done = false; for (int i = 0; !done && device_options[i].name; i++) { /* * Try to find a matching device option. */ if (bstrncasecmp(bp, device_options[i].name, device_options[i].compare_size)) { switch (device_options[i].type) { case argument_profile: m_profile = bp + device_options[i].compare_size; done = true; break; case argument_bucket: m_object_bucketname = bp + device_options[i].compare_size; done = true; break; default: break; } } } if (!done) { Mmsg1(errmsg, _("Unable to parse device option: %s\n"), bp); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } bp = next_option; } if (!m_profile) { Mmsg0(errmsg, _("No droplet profile configured\n")); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } /* * Strip any .profile prefix from the libdroplet profile name. */ len = strlen(m_profile); if (len > 8 && bstrcasecmp(m_profile + (len - 8), ".profile")) { m_profile[len - 8] = '\0'; } } /* * See if we need to setup a new context for this device. */ if (!m_ctx) { char *bp; /* * See if this is a path. */ bp = strrchr(m_object_configstring, '/'); if (!bp) { /* * Only a profile name. */ m_ctx = dpl_ctx_new(NULL, m_object_configstring); } else { if (bp == m_object_configstring) { /* * Profile in root of filesystem */ m_ctx = dpl_ctx_new("/", bp + 1); } else { /* * Profile somewhere else. */ *bp++ = '\0'; m_ctx = dpl_ctx_new(m_object_configstring, bp); } } /* * If we failed to allocate a new context fail the open. */ if (!m_ctx) { Mmsg1(errmsg, _("Failed to create a new context using config %s\n"), dev_options); return -1; } /* * Login if that is needed for this backend. */ status = dpl_login(m_ctx); switch (status) { case DPL_SUCCESS: break; case DPL_ENOTSUPP: /* * Backend doesn't support login which is fine. */ break; default: Mmsg2(errmsg, _("Failed to login for voume %s using dpl_login(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return -1; } /* * If a bucketname was defined set it in the context. */ if (m_object_bucketname) { m_ctx->cur_bucket = m_object_bucketname; } } /* * See if we don't have a file open already. */ if (m_vfd) { dpl_close(m_vfd); m_vfd = NULL; } /* * Create some options for libdroplet. * * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into * no need to let the library allocate memory we * need to free after copying the data. */ memset(&dpl_options, 0, sizeof(dpl_options)); dpl_options.mask |= DPL_OPTION_NOALLOC; if (flags & O_CREAT) { dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR; status = dpl_open(m_ctx, /* context */ getVolCatName(), /* locator */ dpl_flags, /* flags */ &dpl_options, /* options */ NULL, /* condition */ NULL, /* metadata */ NULL, /* sysmd */ NULL, /* query_params */ NULL, /* stream_status */ &m_vfd); } else { dpl_flags = DPL_VFILE_FLAG_RDWR; status = dpl_open(m_ctx, /* context */ getVolCatName(), /* locator */ dpl_flags, /* flags */ &dpl_options, /* options */ NULL, /* condition */ NULL, /* metadata */ NULL, /* sysmd */ NULL, /* query_params */ NULL, /* stream_status */ &m_vfd); } switch (status) { case DPL_SUCCESS: m_offset = 0; return 0; default: Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); m_vfd = NULL; return droplet_errno_to_system_errno(status); } bail_out: return -1; }
/* * Acquire device for writing. We permit multiple writers. * If this is the first one, we read the label. * * Returns: NULL if failed for any reason * dcr if successful. * Note, normally reserve_device_for_append() is called * before this routine. */ DCR *acquire_device_for_append(DCR *dcr) { DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; bool ok = false; bool have_vol = false; Enter(200); init_device_wait_timers(dcr); dev->Lock_acquire(); /* only one job at a time */ dev->Lock(); Dmsg1(100, "acquire_append device is %s\n", dev->is_tape()?"tape": (dev->is_dvd()?"DVD":"disk")); /* * With the reservation system, this should not happen */ if (dev->can_read()) { Mmsg2(jcr->errmsg, "Want to append but %s device %s is busy reading.\n", dev->print_type(), dev->print_name()); Jmsg(jcr, M_FATAL, 0, jcr->errmsg); Dmsg0(50, jcr->errmsg); goto get_out; } dev->clear_unload(); /* * have_vol defines whether or not mount_next_write_volume should * ask the Director again about what Volume to use. */ if (dev->can_append() && dcr->is_suitable_volume_mounted() && strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") != 0) { Dmsg0(190, "device already in append.\n"); /* * At this point, the correct tape is already mounted, so * we do not need to do mount_next_write_volume(), unless * we need to recycle the tape. */ if (dev->num_writers == 0) { dev->VolCatInfo = dcr->VolCatInfo; /* structure assignment */ } have_vol = dcr->is_tape_position_ok(); } if (!have_vol) { dev->rLock(true); block_device(dev, BST_DOING_ACQUIRE); dev->Unlock(); Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId); if (!dcr->mount_next_write_volume()) { if (!job_canceled(jcr)) { /* Reduce "noise" -- don't print if job canceled */ Mmsg2(jcr->errmsg, _("Could not ready %s device %s for append.\n"), dev->print_type(), dev->print_name()); Jmsg(jcr, M_FATAL, 0, jcr->errmsg); Dmsg0(50, jcr->errmsg); } dev->Lock(); unblock_device(dev); goto get_out; } Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num); dev->Lock(); unblock_device(dev); } if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) { Mmsg0(jcr->errmsg, _("generate_plugin_event(bsdEventDeviceOpen) Failed\n")); Jmsg(jcr, M_FATAL, 0, jcr->errmsg); Dmsg0(50, jcr->errmsg); goto get_out; } dev->num_writers++; /* we are now a writer */ if (jcr->NumWriteVolumes == 0) { jcr->NumWriteVolumes = 1; } dev->VolCatInfo.VolCatJobs++; /* increment number of jobs on vol */ Dmsg4(100, "=== nwriters=%d nres=%d vcatjob=%d dev=%s\n", dev->num_writers, dev->num_reserved(), dev->VolCatInfo.VolCatJobs, dev->print_name()); ok = dir_update_volume_info(dcr, false, false); /* send Volume info to Director */ get_out: /* Don't plugin close here, we might have multiple writers */ dcr->clear_reserved(); dev->Unlock(); dev->Unlock_acquire(); Leave(200); return ok ? dcr : NULL; }
/* * Foward space a file * * Returns: true on success * false on failure */ bool generic_tape_device::fsf(int num) { int32_t os_file = 0; struct mtop mt_com; int status = 0; if (!is_open()) { dev_errno = EBADF; Mmsg0(errmsg, _("Bad call to fsf. Device not open\n")); Emsg0(M_FATAL, 0, errmsg); return false; } if (at_eot()) { dev_errno = 0; Mmsg1(errmsg, _("Device %s at End of Tape.\n"), prt_name); return false; } if (at_eof()) { Dmsg0(200, "ST_EOF set on entry to FSF\n"); } Dmsg0(100, "fsf\n"); block_num = 0; /* * If Fast forward space file is set, then we * use MTFSF to forward space and MTIOCGET * to get the file position. We assume that * the SCSI driver will ensure that we do not * forward space past the end of the medium. */ if (has_cap(CAP_FSF) && has_cap(CAP_MTIOCGET) && has_cap(CAP_FASTFSF)) { int my_errno = 0; mt_com.mt_op = MTFSF; mt_com.mt_count = num; status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status < 0) { my_errno = errno; /* save errno */ } else if ((os_file=get_os_tape_file()) < 0) { my_errno = errno; /* save errno */ } if (my_errno != 0) { berrno be; set_eot(); Dmsg0(200, "Set ST_EOT\n"); clrerror(mt_com.mt_op); Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), prt_name, be.bstrerror(my_errno)); Dmsg1(200, "%s", errmsg); return false; } Dmsg1(200, "fsf file=%d\n", os_file); set_ateof(); file = os_file; return true; /* * Here if CAP_FSF is set, and virtually all drives * these days support it, we read a record, then forward * space one file. Using this procedure, which is slow, * is the only way we can be sure that we don't read * two consecutive EOF marks, which means End of Data. */ } else if (has_cap(CAP_FSF)) { POOLMEM *rbuf; int rbuf_len; Dmsg0(200, "FSF has cap_fsf\n"); if (max_block_size == 0) { rbuf_len = DEFAULT_BLOCK_SIZE; } else { rbuf_len = max_block_size; } rbuf = get_memory(rbuf_len); mt_com.mt_op = MTFSF; mt_com.mt_count = 1; while (num-- && !at_eot()) { Dmsg0(100, "Doing read before fsf\n"); if ((status = this->read((char *)rbuf, rbuf_len)) < 0) { if (errno == ENOMEM) { /* tape record exceeds buf len */ status = rbuf_len; /* This is OK */ /* * On IBM drives, they return ENOSPC at EOM instead of EOF status */ } else if (at_eof() && errno == ENOSPC) { status = 0; } else if (has_cap(CAP_IOERRATEOM) && at_eof() && errno == EIO) { if (has_cap(CAP_IBMLINTAPE)) { Dmsg0(100, "Got EIO on read, checking lin_tape sense data\n"); if (check_scsi_at_eod(m_fd)) { Dmsg0(100, "Sense data confirms it's EOD\n"); status = 0; } else { Dmsg0(100, "Not at EOD, might be a real error. Check sense trace from lin_taped logs.\n"); set_eot(); clrerror(-1); Mmsg1(errmsg, _("read error on %s. ERR=Input/Output error.\n"), prt_name); break; } } else { Dmsg0(100, "Got EIO on read, assuming that's due to EOD\n"); status = 0; } } else { berrno be; set_eot(); clrerror(-1); Dmsg2(100, "Set ST_EOT read errno=%d. ERR=%s\n", dev_errno, be.bstrerror()); Mmsg2(errmsg, _("read error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); Dmsg1(100, "%s", errmsg); break; } } if (status == 0) { /* EOF */ Dmsg1(100, "End of File mark from read. File=%d\n", file+1); /* * Two reads of zero means end of tape */ if (at_eof()) { set_eot(); Dmsg0(100, "Set ST_EOT\n"); break; } else { set_ateof(); continue; } } else { /* Got data */ clear_eot(); clear_eof(); } Dmsg0(100, "Doing MTFSF\n"); status = d_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); if (status < 0) { /* error => EOT */ berrno be; set_eot(); Dmsg0(100, "Set ST_EOT\n"); clrerror(mt_com.mt_op); Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), prt_name, be.bstrerror()); Dmsg0(100, "Got < 0 for MTFSF\n"); Dmsg1(100, "%s", errmsg); } else { set_ateof(); } } free_memory(rbuf); /* * No FSF, so use FSR to simulate it */ } else { Dmsg0(200, "Doing FSR for FSF\n"); while (num-- && !at_eot()) { fsr(INT32_MAX); /* returns -1 on EOF or EOT */ } if (at_eot()) { dev_errno = 0; Mmsg1(errmsg, _("Device %s at End of Tape.\n"), prt_name); status = -1; } else { status = 0; } } Dmsg1(200, "Return %d from FSF\n", status); if (at_eof()) { Dmsg0(200, "ST_EOF set on exit FSF\n"); } if (at_eot()) { Dmsg0(200, "ST_EOT set on exit FSF\n"); } Dmsg1(200, "Return from FSF file=%d\n", file); return status == 0; }