uint8_t ssc_report_density_support(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; uint8_t media; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; media = cmd->scb[1] & 0x01; cmd->dbuf_p->sz = 0; MHVTL_DBG(1, "Report %s Density Support (%ld) **", (media) ? "mounted Media" : "Drive", (long)cmd->dbuf_p->serialNo); if (cmd->scb[1] & 0x02) { /* Don't support Medium Type (yet) */ MHVTL_DBG(1, "Medium Type - not currently supported"); mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (media == 1 && lu_priv->tapeLoaded != TAPE_LOADED) { MHVTL_DBG(1, "Media has to be mounted to return media density"); mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } cmd->dbuf_p->sz = get_unaligned_be16(&cmd->scb[7]); cmd->dbuf_p->sz = resp_report_density(lu_priv, media, cmd->dbuf_p); return SAM_STAT_GOOD; }
uint8_t ssc_write_attributes(struct scsi_cmd *cmd) { int sz; struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Write Attributes (%ld) **", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_UNLOADED: mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: cmd->dbuf_p->sz = get_unaligned_be32(&cmd->scb[10]); sz = retrieve_CDB_data(cmd->cdev, cmd->dbuf_p); MHVTL_DBG(1, " --> Expected to read %d bytes" ", read %d", cmd->dbuf_p->sz, sz); if (resp_write_attribute(cmd) > 0) rewriteMAM(sam_stat); break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; }
uint8_t ssc_erase(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Erasing (%ld) **", (long)cmd->dbuf_p->serialNo); current_state = MHVTL_STATE_ERASE; if (!lu_priv->pm->check_restrictions(cmd)) return SAM_STAT_CHECK_CONDITION; if (c_pos->blk_number != 0) { MHVTL_LOG("Not at BOT.. Can't erase unless at BOT"); mkSenseBuf(NOT_READY, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (OK_to_write) format_tape(sam_stat); else { MHVTL_LOG("Attempt to erase Write-protected media"); mkSenseBuf(NOT_READY, E_MEDIUM_OVERWRITE_ATTEMPTED, sam_stat); return SAM_STAT_CHECK_CONDITION; } return SAM_STAT_GOOD; }
uint8_t ssc_read_position(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; int service_action; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Read Position (%ld) **", (long)cmd->dbuf_p->serialNo); service_action = cmd->scb[1] & 0x1f; /* service_action == 0 or 1 -> Returns 20 bytes of data (short) */ *sam_stat = SAM_STAT_GOOD; switch (lu_priv->tapeLoaded) { case TAPE_LOADED: if ((service_action == 0) || (service_action == 1)) cmd->dbuf_p->sz = resp_read_position(c_pos->blk_number, cmd->dbuf_p->data, sam_stat); break; case TAPE_UNLOADED: mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return *sam_stat; }
uint8_t ssc_read_media_sn(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Read Medium Serial No. (%ld) **", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_LOADED: cmd->dbuf_p->sz = resp_read_media_serial(lu_priv->mediaSerialNo, cmd->dbuf_p->data, sam_stat); break; case TAPE_UNLOADED: mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return *sam_stat; }
static int read_header(uint32_t blk_number, uint8_t *sam_stat) { loff_t nread; if (blk_number > eod_blk_number) { MHVTL_ERR("Attempt to seek [%d] beyond EOD [%d]", blk_number, eod_blk_number); } else if (blk_number == eod_blk_number) { mkEODHeader(eod_blk_number, eod_data_offset); } else { nread = pread(indxfile, &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nread < 0) { MHVTL_ERR("Medium format corrupt"); mkSenseBuf(MEDIUM_ERROR,E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } else if (nread != sizeof(raw_pos)) { MHVTL_ERR("Failed to read next header"); mkSenseBuf(MEDIUM_ERROR, E_END_OF_DATA, sam_stat); return -1; } } MHVTL_DBG(3, "Reading header %d at offset %ld, type: %s, size: %d", raw_pos.hdr.blk_number, (unsigned long)raw_pos.data_offset, mhvtl_block_type_desc(raw_pos.hdr.blk_type), raw_pos.hdr.blk_size); return 0; }
uint8_t ssc_rewind(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; int retval; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Rewinding (%ld) **", (long)cmd->dbuf_p->serialNo); current_state = MHVTL_STATE_REWIND; switch (lu_priv->tapeLoaded) { case TAPE_UNLOADED: mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: retval = rewind_tape(sam_stat); if (retval < 0) { mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; }
uint8_t ssc_allow_overwrite(struct scsi_cmd *cmd) { uint8_t *cdb = cmd->scb; uint8_t allow_overwrite = cdb[2] & 0x0f; uint8_t partition = cdb[3]; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t ret_stat = SAM_STAT_GOOD; uint64_t allow_overwrite_block; struct priv_lu_ssc *lu_ssc; lu_ssc = cmd->lu->lu_private; if (allow_overwrite > 2) /* Truncate bad values 3 to 15 -> '3' */ allow_overwrite = 3; MHVTL_DBG(1, "ALLOW OVERWRITE (%ld) : %s **", (long)cmd->dbuf_p->serialNo, allow_overwrite_desc[allow_overwrite].desc); lu_ssc->allow_overwrite = FALSE; switch (allow_overwrite) { case 0: break; case 1: /* current position */ if (partition) { /* Paritions not supported at this stage */ MHVTL_LOG("Partitions not implemented at this time"); mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } allow_overwrite_block = get_unaligned_be64(&cdb[4]); MHVTL_DBG(1, "Allow overwrite block: %lld", (long long)allow_overwrite_block); if (allow_overwrite_block == current_tape_block()) { lu_ssc->allow_overwrite_block = allow_overwrite_block; lu_ssc->allow_overwrite = TRUE; } else { /* Set allow_overwrite position to an invalid number */ lu_ssc->allow_overwrite_block = 0; lu_ssc->allow_overwrite_block--; mkSenseBuf(ILLEGAL_REQUEST, E_SEQUENTIAL_POSITIONING_ERROR, sam_stat); ret_stat = SAM_STAT_CHECK_CONDITION; } break; case 2: lu_ssc->allow_overwrite = 2; break; default: mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); ret_stat = SAM_STAT_CHECK_CONDITION; break; } return ret_stat; }
int position_blocks_forw(uint32_t count, uint8_t *sam_stat) { uint32_t residual; uint32_t blk_target; unsigned int i; if (!tape_loaded(sam_stat)) { return -1; } if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; blk_target = raw_pos.hdr.blk_number + count; /* Find the first filemark forward from our current position, if any. */ for (i = 0; i < meta.filemark_count; i++) { MHVTL_DBG(3, "filemark at %ld", (unsigned long)filemarks[i]); if (filemarks[i] >= raw_pos.hdr.blk_number) { break; } } /* If there is one, see if it is between our current position and our desired destination. */ if (i < meta.filemark_count) { if (filemarks[i] >= blk_target) { return position_to_block(blk_target, sam_stat); } residual = blk_target - raw_pos.hdr.blk_number + 1; if (read_header(filemarks[i] + 1, sam_stat)) { return -1; } MHVTL_DBG(1, "Filemark encountered: block %d", filemarks[i]); mkSenseBuf(NO_SENSE | SD_FILEMARK, E_MARK, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } if (blk_target > eod_blk_number) { residual = blk_target - eod_blk_number; if (read_header(eod_blk_number, sam_stat)) { return -1; } MHVTL_DBG(1, "EOD encountered"); mkSenseBuf(BLANK_CHECK, E_END_OF_DATA, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } return position_to_block(blk_target, sam_stat); }
static int check_for_overwrite(uint8_t *sam_stat) { uint32_t blk_number; uint64_t data_offset; unsigned int i; if (raw_pos.hdr.blk_type == B_EOD) { return 0; } MHVTL_DBG(2, "At block %ld", (unsigned long)raw_pos.hdr.blk_number); /* We aren't at EOD so we are performing a rewrite. Truncate the data and index files back to the current length. */ blk_number = raw_pos.hdr.blk_number; data_offset = raw_pos.data_offset; if (ftruncate(indxfile, blk_number * sizeof(raw_pos))) { mkSenseBuf(MEDIUM_ERROR, E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file ftruncate failure, pos: " "%" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } if (ftruncate(datafile, data_offset)) { mkSenseBuf(MEDIUM_ERROR, E_WRITE_ERROR, sam_stat); MHVTL_ERR("Data file ftruncate failure, pos: " "%" PRId64 ": %s", data_offset, strerror(errno)); return -1; } /* Update the filemark map removing any filemarks which will be overwritten. Rewrite the filemark map so that the on-disk image of the map is consistent with the new sizes of the other two files. */ for (i = 0; i < meta.filemark_count; i++) { MHVTL_DBG(2, "filemarks[%d] %d", i, filemarks[i]); if (filemarks[i] >= blk_number) { MHVTL_DBG(2, "Setting filemark_count from %d to %d", meta.filemark_count, i); meta.filemark_count = i; return rewrite_meta_file(); } } return 0; }
uint8_t ssc_read_block_limits(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Read block limits (%ld) **", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_LOADED: case TAPE_UNLOADED: cmd->dbuf_p->sz = resp_read_block_limits(cmd->dbuf_p, lu_priv->bufsize); break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; }
int position_filemarks_back(uint32_t count, uint8_t *sam_stat) { uint32_t residual; int i; if (!tape_loaded(sam_stat)) { return -1; } if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; /* Find the block number of the first filemark less than our current position. */ for (i = meta.filemark_count - 1; i >= 0; i--) { if (filemarks[i] < raw_pos.hdr.blk_number) { break; } } if (i + 1 >= count) { return position_to_block(filemarks[i - count + 1], sam_stat); } else { residual = count - i - 1; if (read_header(0, sam_stat)) { return -1; } mkSenseBuf(NO_SENSE | SD_EOM, E_BOM, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } }
int position_filemarks_forw(uint32_t count, uint8_t *sam_stat) { uint32_t residual; unsigned int i; if (!tape_loaded(sam_stat)) { return -1; } if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; /* Find the block number of the first filemark greater than our current position. */ for (i = 0; i < meta.filemark_count; i++) { if (filemarks[i] >= raw_pos.hdr.blk_number) { break; } } if (i + count - 1 < meta.filemark_count) { return position_to_block(filemarks[i + count - 1] + 1, sam_stat); } else { residual = i + count - meta.filemark_count; if (read_header(eod_blk_number, sam_stat)) { return -1; } mkSenseBuf(BLANK_CHECK, E_END_OF_DATA, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } }
int position_to_block(uint32_t blk_number, uint8_t *sam_stat) { if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(2, "Position to block %d", blk_number); if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; if (blk_number > eod_blk_number) { mkSenseBuf(BLANK_CHECK, E_END_OF_DATA, sam_stat); MHVTL_DBG(1, "End of data detected while positioning"); return position_to_eod(sam_stat); } /* Treat a position to block zero specially, as it has different semantics than other blocks when the tape is WORM. */ if (blk_number == 0) return rewind_tape(sam_stat); else return read_header(blk_number, sam_stat); }
static int tape_loaded(uint8_t *sam_stat) { if (datafile != -1) { return 1; } mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return 0; }
uint8_t ssc_load_unload(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; int load; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; load = cmd->scb[4] & 0x01; current_state = (load) ? MHVTL_STATE_LOADING : MHVTL_STATE_UNLOADING; if (cmd->scb[4] & 0x04) { /* EOT bit */ MHVTL_ERR("EOT bit set on load. Not supported"); mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } MHVTL_DBG(1, "%s tape (%ld) **", (load) ? "Loading" : "Unloading", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_UNLOADED: if (load) rewind_tape(sam_stat); else { mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; } break; case TAPE_LOADED: if (!load) unloadTape(sam_stat); break; default: mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } return SAM_STAT_GOOD; }
uint8_t valid_encryption_blk(struct scsi_cmd *cmd) { uint8_t correct_key; int i; struct lu_phy_attr *lu = cmd->lu; struct priv_lu_ssc *lu_priv; struct encryption *encr; uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; lu_priv = lu->lu_private; encr = lu_priv->encr; /* decryption logic */ correct_key = TRUE; if (c_pos->blk_flags & BLKHDR_FLG_ENCRYPTED) { /* compare the keys */ if (lu_priv->DECRYPT_MODE > 1) { if (c_pos->encryption.key_length != KEY_LENGTH) { mkSenseBuf(DATA_PROTECT, E_INCORRECT_KEY, sam_stat); correct_key = FALSE; } for (i = 0; i < c_pos->encryption.key_length; ++i) { if (c_pos->encryption.key[i] != KEY[i]) { mkSenseBuf(DATA_PROTECT, E_INCORRECT_KEY, sam_stat); correct_key = FALSE; return correct_key; } } } else { mkSenseBuf(DATA_PROTECT, E_UNABLE_TO_DECRYPT, sam_stat); correct_key = FALSE; } } else if (lu_priv->DECRYPT_MODE == 2) { mkSenseBuf(DATA_PROTECT, E_UNENCRYPTED_DATA, sam_stat); correct_key = FALSE; } return correct_key; }
uint8_t ssc_read_attributes(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; MHVTL_DBG(1, "Read Attribute (%ld) **", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_UNLOADED: MHVTL_DBG(1, "Failed due to \"no media loaded\""); mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: break; default: MHVTL_DBG(1, "Failed due to \"media corrupt\""); mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); return SAM_STAT_CHECK_CONDITION; break; } /* Only support Service Action - Attribute Values */ if (cmd->scb[1] > 1) { mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } cmd->dbuf_p->sz = resp_read_attribute(cmd); return SAM_STAT_GOOD; }
uint32_t read_tape_block(uint8_t *buf, uint32_t buf_size, uint8_t *sam_stat) { loff_t nread; uint32_t iosize; if (!tape_loaded(sam_stat)) return -1; MHVTL_DBG(3, "Reading blk %ld, size: %d", (unsigned long)raw_pos.hdr.blk_number, buf_size); /* The caller should have already verified that this is a B_DATA block before issuing this read, so we shouldn't have to worry about B_EOD or B_FILEMARK here. */ if (raw_pos.hdr.blk_type == B_EOD) { mkSenseBuf(BLANK_CHECK, E_END_OF_DATA, sam_stat); MHVTL_ERR("End of data detected while reading"); return -1; } iosize = raw_pos.hdr.disk_blk_size; if (iosize > buf_size) iosize = buf_size; nread = pread(datafile, buf, iosize, raw_pos.data_offset); if (nread != iosize) { MHVTL_ERR("Failed to read %d bytes", iosize); return -1; } // Now position to the following block. if (read_header(raw_pos.hdr.blk_number + 1, sam_stat)) { MHVTL_ERR("Failed to read block header %d", raw_pos.hdr.blk_number + 1); return -1; } return nread; }
int rewriteMAM(uint8_t *sam_stat) { loff_t nwrite = 0; if (!tape_loaded(sam_stat)) { return -1; } // Rewrite MAM data nwrite = pwrite(metafile, &mam, sizeof(mam), 0); if (nwrite != sizeof(mam)) { mkSenseBuf(MEDIUM_ERROR, E_MEDIUM_FMT_CORRUPT, sam_stat); return -1; } return nwrite; }
uint8_t ssc_write_filemarks(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; int count; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; count = get_unaligned_be24(&cmd->scb[2]); MHVTL_DBG(1, "Write %d filemarks (%ld) **", count, (long)cmd->dbuf_p->serialNo); if (!lu_priv->pm->check_restrictions(cmd)) { /* If restrictions & WORM media at block 0.. OK * Otherwise return CHECK_CONDITION. * check_restrictions() * was nice enough to set correct sense status for us. */ if ((mam.MediumType == MEDIA_TYPE_WORM) && (c_pos->blk_number == 0)) { MHVTL_DBG(1, "Erasing WORM media"); } else return SAM_STAT_CHECK_CONDITION; } write_filemarks(count, sam_stat); if (count) { if (current_tape_offset() >= get_unaligned_be64(&mam.max_capacity)) { mam.remaining_capacity = 0L; MHVTL_DBG(2, "Setting EOM flag"); mkSenseBuf(NO_SENSE|SD_EOM, NO_ADDITIONAL_SENSE, sam_stat); return SAM_STAT_CHECK_CONDITION; } } return SAM_STAT_GOOD; }
uint8_t ssc_format_media(struct scsi_cmd *cmd) { struct lu_phy_attr *lu; struct priv_lu_ssc *lu_priv; lu = cmd->lu; lu_priv = lu->lu_private; MHVTL_DBG(1, "Format Medium (%ld) **", (long)cmd->dbuf_p->serialNo); if (!lu_priv->pm->check_restrictions(cmd)) return SAM_STAT_CHECK_CONDITION; if (c_pos->blk_number != 0) { MHVTL_DBG(2, "Not at beginning **"); mkSenseBuf(ILLEGAL_REQUEST, E_POSITION_PAST_BOM, &cmd->dbuf_p->sam_stat); return SAM_STAT_CHECK_CONDITION; } format_tape(&cmd->dbuf_p->sam_stat); return SAM_STAT_GOOD; }
/* * * Process the SCSI command * * Called with: * cdev -> Char dev file handle, * cdb -> SCSI Command buffer pointer, * struct vtl_ds -> general purpose data structure... Need better name * * Return: * SAM status returned in struct vtl_ds.sam_stat */ static void processCommand(int cdev, uint8_t *cdb, struct vtl_ds *dbuf_p, useconds_t pollInterval) { struct scsi_cmd _cmd; struct scsi_cmd *cmd; cmd = &_cmd; cmd->scb = cdb; cmd->scb_len = 16; /* fixme */ cmd->dbuf_p = dbuf_p; cmd->lu = &lunit; cmd->pollInterval = pollInterval; MHVTL_DBG_PRT_CDB(1, cmd); switch (cdb[0]) { case REPORT_LUNS: case REQUEST_SENSE: case MODE_SELECT: case INQUIRY: dbuf_p->sam_stat = SAM_STAT_GOOD; break; default: if (cmd->lu->online == 0) { mkSenseBuf(NOT_READY, E_OFFLINE, &dbuf_p->sam_stat); return; } if (check_reset(&dbuf_p->sam_stat)) return; } if (cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform) cmd->lu->scsi_ops->ops[cdb[0]].pre_cmd_perform(cmd, NULL); dbuf_p->sam_stat = cmd->lu->scsi_ops->ops[cdb[0]].cmd_perform(cmd); return; }
uint8_t valid_encryption_media(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; struct lu_phy_attr *lu; struct priv_lu_ssc *lu_priv; lu = cmd->lu; lu_priv = lu->lu_private; if (c_pos->blk_number == 0) { modeBlockDescriptor[0] = lu_priv->pm->native_drive_density->density; mam.MediumDensityCode = modeBlockDescriptor[0]; mam.FormattedDensityCode = modeBlockDescriptor[0]; rewriteMAM(sam_stat); } else { if (mam.MediumDensityCode != lu_priv->pm->native_drive_density->density) { mkSenseBuf(DATA_PROTECT, E_WRITE_PROTECT, sam_stat); return SAM_STAT_CHECK_CONDITION; } } return SAM_STAT_GOOD; }
uint8_t ssc_tur(struct scsi_cmd *cmd) { struct priv_lu_ssc *lu_priv; uint8_t *sam_stat; char str[64]; lu_priv = cmd->lu->lu_private; sam_stat = &cmd->dbuf_p->sam_stat; *sam_stat = SAM_STAT_GOOD; sprintf(str, "Test Unit Ready (%ld) ** : ", (long)cmd->dbuf_p->serialNo); switch (lu_priv->tapeLoaded) { case TAPE_UNLOADED: strcat(str, "No, No tape loaded"); mkSenseBuf(NOT_READY, E_MEDIUM_NOT_PRESENT, sam_stat); *sam_stat = SAM_STAT_CHECK_CONDITION; break; case TAPE_LOADED: if (mam.MediumType == MEDIA_TYPE_CLEAN) { int state; strcat(str, "No, Cleaning cart loaded"); if (lu_priv->cleaning_media_state) state = *lu_priv->cleaning_media_state; else state = 0; switch (state) { case CLEAN_MOUNT_STAGE1: mkSenseBuf(NOT_READY, E_CLEANING_CART_INSTALLED, sam_stat); break; case CLEAN_MOUNT_STAGE2: mkSenseBuf(NOT_READY, E_CAUSE_NOT_REPORTABLE, sam_stat); break; case CLEAN_MOUNT_STAGE3: mkSenseBuf(NOT_READY, E_INITIALIZING_REQUIRED, sam_stat); break; default: MHVTL_ERR("Unknown cleaning media mount state"); mkSenseBuf(NOT_READY, E_CLEANING_CART_INSTALLED, sam_stat); break; } *sam_stat = SAM_STAT_CHECK_CONDITION; } else strcat(str, "Yes"); break; default: strcat(str, "No, Media format corrupt"); mkSenseBuf(NOT_READY, E_MEDIUM_FMT_CORRUPT, sam_stat); *sam_stat = SAM_STAT_CHECK_CONDITION; break; } MHVTL_DBG(1, "%s", str); return *sam_stat; }
int load_tape(const char *pcl, uint8_t *sam_stat) { char pcl_data[1024], pcl_indx[1024], pcl_meta[1024]; struct stat data_stat, indx_stat, meta_stat; uint64_t exp_size; size_t io_size; loff_t nread; int rc = 0; /* KFRDEBUG - sam_stat needs updates in lots of places here. */ /* If some other PCL is already open, return. */ if (datafile >= 0) return 1; /* Open all three files and stat them to get their current sizes. */ if (strlen(home_directory)) snprintf(currentPCL, ARRAY_SIZE(currentPCL), "%s/%s", home_directory, pcl); else snprintf(currentPCL, ARRAY_SIZE(currentPCL), "%s/%s", MHVTL_HOME_PATH, pcl); snprintf(pcl_data, ARRAY_SIZE(pcl_data), "%s/data", currentPCL); snprintf(pcl_indx, ARRAY_SIZE(pcl_indx), "%s/indx", currentPCL); snprintf(pcl_meta, ARRAY_SIZE(pcl_meta), "%s/meta", currentPCL); MHVTL_DBG(2, "Opening media: %s", pcl); if (stat(pcl_data, &data_stat) == -1) { MHVTL_DBG(2, "Couldn't find %s, trying previous default: %s/%s", pcl_data, MHVTL_HOME_PATH, pcl); snprintf(currentPCL, ARRAY_SIZE(currentPCL), "%s/%s", MHVTL_HOME_PATH, pcl); snprintf(pcl_data, ARRAY_SIZE(pcl_data), "%s/data", currentPCL); snprintf(pcl_indx, ARRAY_SIZE(pcl_indx), "%s/indx", currentPCL); snprintf(pcl_meta, ARRAY_SIZE(pcl_meta), "%s/meta", currentPCL); } if ((datafile = open(pcl_data, O_RDWR|O_LARGEFILE)) == -1) { MHVTL_ERR("open of pcl %s file %s failed, %s", pcl, pcl_data, strerror(errno)); rc = 3; goto failed; } if ((indxfile = open(pcl_indx, O_RDWR|O_LARGEFILE)) == -1) { MHVTL_ERR("open of pcl %s file %s failed, %s", pcl, pcl_indx, strerror(errno)); rc = 3; goto failed; } if ((metafile = open(pcl_meta, O_RDWR|O_LARGEFILE)) == -1) { MHVTL_ERR("open of pcl %s file %s failed, %s", pcl, pcl_meta, strerror(errno)); rc = 3; goto failed; } if (fstat(datafile, &data_stat) < 0) { MHVTL_ERR("stat of pcl %s file %s failed: %s", pcl, pcl_data, strerror(errno)); rc = 3; goto failed; } if (fstat(indxfile, &indx_stat) < 0) { MHVTL_ERR("stat of pcl %s file %s failed: %s", pcl, pcl_indx, strerror(errno)); rc = 3; goto failed; } if (fstat(metafile, &meta_stat) < 0) { MHVTL_ERR("stat of pcl %s file %s failed: %s", pcl, pcl_meta, strerror(errno)); rc = 3; goto failed; } /* Verify that the metafile size is at least reasonable. */ exp_size = sizeof(mam) + sizeof(meta); if ((uint32_t)meta_stat.st_size < exp_size) { MHVTL_ERR("pcl %s file %s is not the correct length, " "expected at least %" PRId64 ", actual %" PRId64, pcl, pcl_meta, exp_size, meta_stat.st_size); rc = 2; goto failed; } /* Read in the MAM and sanity-check it. */ if ((nread = read(metafile, &mam, sizeof(mam))) < 0) { MHVTL_ERR("Error reading pcl %s MAM from metafile: %s", pcl, strerror(errno)); rc = 2; goto failed; } else if (nread != sizeof(mam)) { MHVTL_ERR("Error reading pcl %s MAM from metafile: " "unexpected read length", pcl); rc = 2; goto failed; } if (mam.tape_fmt_version != TAPE_FMT_VERSION) { MHVTL_ERR("pcl %s MAM contains incorrect media format", pcl); mkSenseBuf(MEDIUM_ERROR, E_MEDIUM_FMT_CORRUPT, sam_stat); rc = 2; goto failed; } /* Read in the meta_header structure and sanity-check it. */ if ((nread = read(metafile, &meta, sizeof(meta))) < 0) { MHVTL_ERR("Error reading pcl %s meta_header from " "metafile: %s", pcl, strerror(errno)); rc = 2; goto failed; } else if (nread != sizeof(meta)) { MHVTL_ERR("Error reading pcl %s meta header from " "metafile: unexpected read length", pcl); rc = 2; goto failed; } /* Now recompute the correct size of the meta file. */ exp_size = sizeof(mam) + sizeof(meta) + (meta.filemark_count * sizeof(*filemarks)); if ((uint32_t)meta_stat.st_size != exp_size) { MHVTL_ERR("pcl %s file %s is not the correct length, " "expected %" PRId64 ", actual %" PRId64, pcl, pcl_meta, exp_size, meta_stat.st_size); rc = 2; goto failed; } /* See if we have allocated enough space for the actual number of filemarks on the tape. If not, realloc now. */ if (check_filemarks_alloc(meta.filemark_count)) { rc = 3; goto failed; } /* Now read in the filemark map. */ io_size = meta.filemark_count * sizeof(*filemarks); if (io_size == 0) { /* do nothing */ } else if ((nread = read(metafile, filemarks, io_size)) < 0) { MHVTL_ERR("Error reading pcl %s filemark map from " "metafile: %s", pcl, strerror(errno)); rc = 2; goto failed; } else if ((size_t)nread != io_size) { MHVTL_ERR("Error reading pcl %s filemark map from " "metafile: unexpected read length", pcl); rc = 2; goto failed; } /* Use the size of the indx file to work out where the virtual B_EOD block resides. */ if ((indx_stat.st_size % sizeof(struct raw_header)) != 0) { MHVTL_ERR("pcl %s indx file has improper length, indicating " "possible file corruption", pcl); rc = 2; goto failed; } eod_blk_number = indx_stat.st_size / sizeof(struct raw_header); /* Make sure that the filemark map is consistent with the size of the indx file. */ if (meta.filemark_count > 0 && filemarks[meta.filemark_count - 1] >= eod_blk_number) { MHVTL_ERR("pcl %s indx file has improper length as compared " "to the meta file, indicating possible file corruption", pcl); rc = 2; goto failed; } /* Read in the last raw_header struct from the indx file and use that to validate the correct size of the data file. */ if (eod_blk_number == 0) { eod_data_offset = 0; } else { if (read_header(eod_blk_number - 1, sam_stat)) { rc = 3; goto failed; } eod_data_offset = raw_pos.data_offset + raw_pos.hdr.disk_blk_size; } if ((uint64_t)data_stat.st_size != eod_data_offset) { MHVTL_ERR("pcl %s file %s is not the correct length, " "expected %" PRId64 ", actual %" PRId64, pcl, pcl_data, eod_data_offset, data_stat.st_size); rc = 2; goto failed; } /* Give a hint to the kernel that data, once written, tends not to be accessed again immediately. */ posix_fadvise(indxfile, 0, 0, POSIX_FADV_DONTNEED); posix_fadvise(datafile, 0, 0, POSIX_FADV_DONTNEED); /* Now initialize raw_pos by reading in the first header, if any. */ if (read_header(0, sam_stat)) { rc = 3; goto failed; } return 0; failed: if (datafile >= 0) { close(datafile); datafile = -1; } if (indxfile >= 0) { close(indxfile); indxfile = -1; } if (metafile >= 0) { close(metafile); metafile = -1; } return rc; }
/* * Process the MODE_SELECT command */ uint8_t ssc_mode_select(struct scsi_cmd *cmd) { uint8_t *sam_stat = &cmd->dbuf_p->sam_stat; uint8_t *buf = cmd->dbuf_p->data; int block_descriptor_sz; int page_len; uint8_t *bdb = NULL; int i; int long_lba = 0; int count; int save_page; save_page = cmd->scb[1] & 0x01; switch (cmd->scb[0]) { case MODE_SELECT: cmd->dbuf_p->sz = cmd->scb[4]; break; case MODE_SELECT_10: cmd->dbuf_p->sz = get_unaligned_be16(&cmd->scb[7]); break; default: cmd->dbuf_p->sz = 0; } count = retrieve_CDB_data(cmd->cdev, cmd->dbuf_p); MHVTL_DBG(1, "MODE SELECT (%ld) **", (long)cmd->dbuf_p->serialNo); if (!(cmd->scb[1] & 0x10)) { /* Page Format: 1 - SPC, 0 - vendor uniq */ mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); return SAM_STAT_CHECK_CONDITION; } switch (cmd->scb[0]) { case MODE_SELECT: block_descriptor_sz = buf[3]; if (block_descriptor_sz) bdb = &buf[4]; i = 4 + block_descriptor_sz; break; case MODE_SELECT_10: block_descriptor_sz = get_unaligned_be16(&buf[6]); long_lba = buf[4] & 1; if (block_descriptor_sz) bdb = &buf[8]; i = 8 + block_descriptor_sz; break; default: mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_OP_CODE, sam_stat); return SAM_STAT_CHECK_CONDITION; } if (bdb) { if (long_lba) { mkSenseBuf(ILLEGAL_REQUEST, E_INVALID_FIELD_IN_CDB, sam_stat); MHVTL_DBG(1, "Warning can not " "handle long descriptor block (long_lba bit)"); return SAM_STAT_CHECK_CONDITION; } memcpy(modeBlockDescriptor, bdb, block_descriptor_sz); } /* Ignore mode pages if 'save page' bit not set */ if (!save_page) { MHVTL_DBG(1, "Save page bit not set. Ignoring page data"); return SAM_STAT_GOOD; } #ifdef MHVTL_DEBUG if (debug) hex_dump(buf, cmd->dbuf_p->sz); #endif MHVTL_DBG(3, "count: %d, i: %d", count, i); if (i == 4) { MHVTL_DBG(3, "Offset 0: %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3]); } else { MHVTL_DBG(3, "Offset 0: %02x %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); } count -= i; while (i < count) { MHVTL_DBG(3, " %02d: %02x %02x %02x %02x %02x %02x %02x %02x", i, buf[i+0], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7]); MHVTL_DBG(3, " %02d: %02x %02x %02x %02x %02x %02x %02x %02x", i+8, buf[i+8], buf[i+9], buf[i+10], buf[i+11], buf[i+12], buf[i+13], buf[i+14], buf[i+15]); MHVTL_DBG(3, " %02d: %02x %02x %02x %02x %02x %02x %02x %02x", i+16, buf[i+16], buf[i+17], buf[i+18], buf[i+19], buf[i+20], buf[i+21], buf[i+22], buf[i+23]); MHVTL_DBG(3, " %02d: %02x %02x %02x %02x %02x %02x %02x %02x", i+24, buf[i+24], buf[i+25], buf[i+26], buf[i+27], buf[i+28], buf[i+29], buf[i+30], buf[i+31]); /* Default page len is, override if sub-pages */ page_len = buf[i + 1]; switch (buf[i]) { case MODE_DATA_COMPRESSION: set_mode_compression(cmd, &buf[i]); break; case MODE_DEVICE_CONFIGURATION: /* If this is '01' it's a subpage value * i.e. DEVICE CONFIGURATION EXTENSION * If it's 0x0e, it indicates a page length * for MODE DEVICE CONFIGURATION */ if (buf[i + 1] == 0x01) { if (set_device_configuration_extension(cmd, &buf[i])) return SAM_STAT_CHECK_CONDITION; /* Subpage 1 - override default page length */ page_len = get_unaligned_be16(&buf[i + 2]); } else set_device_configuration(cmd, &buf[i]); break; default: MHVTL_DBG_PRT_CDB(1, cmd); MHVTL_DBG(1, "Mode page 0x%02x not handled", buf[i]); break; } if (page_len == 0) { /* Something wrong with data structure */ page_len = cmd->dbuf_p->sz; MHVTL_LOG("Problem with mode select data structure"); } i += page_len; /* Next mode page */ } return SAM_STAT_GOOD; }
int write_filemarks(uint32_t count, uint8_t *sam_stat) { uint32_t blk_number; uint64_t data_offset; ssize_t nwrite; if (!tape_loaded(sam_stat)) { return -1; } /* Applications assume that writing a filemark (even writing zero filemarks) will force-flush any data buffered in the drive to media so that after the write-filemarks call returns there is no possibility that any data previously written could be lost due to a power hit. Provide a similar guarantee here. */ if (count == 0) { MHVTL_DBG(2, "Flushing data - 0 filemarks written"); fsync(datafile); fsync(indxfile); fsync(metafile); return 0; } if (check_for_overwrite(sam_stat)) { return -1; } /* Preserve existing raw_pos data we need, then clear raw_pos and fill it in with new data. */ blk_number = raw_pos.hdr.blk_number; data_offset = raw_pos.data_offset; memset(&raw_pos, 0, sizeof(raw_pos)); raw_pos.data_offset = data_offset; raw_pos.hdr.blk_type = B_FILEMARK; /* Header type */ raw_pos.hdr.blk_flags = 0; raw_pos.hdr.blk_number = blk_number; raw_pos.hdr.blk_size = 0; raw_pos.hdr.disk_blk_size = 0; /* Now write out one header per filemark. */ for ( ; count > 0; count--, blk_number++) { raw_pos.hdr.blk_number = blk_number; MHVTL_DBG(3, "Writing filemark: block %d", blk_number); nwrite = pwrite(indxfile, &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nwrite != sizeof(raw_pos)) { mkSenseBuf(MEDIUM_ERROR, E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file write failure," " pos: %" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } add_filemark(blk_number); } /* Provide the force-flush guarantee. */ fsync(datafile); fsync(indxfile); fsync(metafile); return mkEODHeader(blk_number, data_offset); }
int position_blocks_back(uint32_t count, uint8_t *sam_stat) { uint32_t residual; uint32_t blk_target; int i = -1; unsigned int num_filemarks = meta.filemark_count; if (!tape_loaded(sam_stat)) return -1; if (mam.MediumType == MEDIA_TYPE_WORM) OK_to_write = 0; MHVTL_DBG(2, "Position before movement: %d", raw_pos.hdr.blk_number); if (count < raw_pos.hdr.blk_number) blk_target = raw_pos.hdr.blk_number - count; else blk_target = 0; /* Find the first filemark prior to our current position, if any. */ if (num_filemarks > 0) { for (i = num_filemarks - 1; i <= (int)num_filemarks; i--) { MHVTL_DBG(3, "filemark at %ld", (unsigned long)filemarks[i]); if (filemarks[i] < raw_pos.hdr.blk_number) break; } } /* If there is one, see if it is between our current position and our desired destination. */ if (i >= 0) { if (filemarks[i] < blk_target) return position_to_block(blk_target, sam_stat); residual = raw_pos.hdr.blk_number - blk_target; if (read_header(filemarks[i], sam_stat)) return -1; MHVTL_DBG(2, "Filemark encountered: block %d", filemarks[i]); mkSenseBuf(NO_SENSE | SD_FILEMARK, E_MARK, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } if (count > raw_pos.hdr.blk_number) { residual = count - raw_pos.hdr.blk_number; if (read_header(0, sam_stat)) return -1; MHVTL_DBG(1, "BOM encountered"); mkSenseBuf(NO_SENSE | SD_EOM, E_BOM, sam_stat); put_unaligned_be32(residual, &sense[3]); return -1; } return position_to_block(blk_target, sam_stat); }
int write_tape_block(const uint8_t *buffer, uint32_t blk_size, uint32_t comp_size, const struct encryption *encryptp, uint8_t comp_type, uint8_t *sam_stat) { uint32_t blk_number, disk_blk_size; uint64_t data_offset; ssize_t nwrite; if (!tape_loaded(sam_stat)) { return -1; } if (check_for_overwrite(sam_stat)) { return -1; } /* Preserve existing raw_pos data we need, then clear out raw_pos and fill it in with new data. */ blk_number = raw_pos.hdr.blk_number; data_offset = raw_pos.data_offset; memset(&raw_pos, 0, sizeof(raw_pos)); raw_pos.data_offset = data_offset; raw_pos.hdr.blk_type = B_DATA; /* Header type */ raw_pos.hdr.blk_flags = 0; raw_pos.hdr.blk_number = blk_number; raw_pos.hdr.blk_size = blk_size; /* Size of uncompressed data */ if (comp_size) { if (comp_type == LZO) raw_pos.hdr.blk_flags |= BLKHDR_FLG_LZO_COMPRESSED; else raw_pos.hdr.blk_flags |= BLKHDR_FLG_ZLIB_COMPRESSED; raw_pos.hdr.disk_blk_size = disk_blk_size = comp_size; } else { raw_pos.hdr.disk_blk_size = disk_blk_size = blk_size; } if (encryptp != NULL) { unsigned int i; raw_pos.hdr.blk_flags |= BLKHDR_FLG_ENCRYPTED; raw_pos.hdr.encryption.ukad_length = encryptp->ukad_length; for (i = 0; i < encryptp->ukad_length; ++i) { raw_pos.hdr.encryption.ukad[i] = encryptp->ukad[i]; } raw_pos.hdr.encryption.akad_length = encryptp->akad_length; for (i = 0; i < encryptp->akad_length; ++i) { raw_pos.hdr.encryption.akad[i] = encryptp->akad[i]; } raw_pos.hdr.encryption.key_length = encryptp->key_length; for (i = 0; i < encryptp->key_length; ++i) { raw_pos.hdr.encryption.key[i] = encryptp->key[i]; } } /* Now write out both the header and the data. */ nwrite = pwrite(indxfile, &raw_pos, sizeof(raw_pos), blk_number * sizeof(raw_pos)); if (nwrite != sizeof(raw_pos)) { mkSenseBuf(MEDIUM_ERROR, E_WRITE_ERROR, sam_stat); MHVTL_ERR("Index file write failure, pos: %" PRId64 ": %s", (uint64_t)blk_number * sizeof(raw_pos), strerror(errno)); return -1; } nwrite = pwrite(datafile, buffer, disk_blk_size, data_offset); if (nwrite != disk_blk_size) { mkSenseBuf(MEDIUM_ERROR, E_WRITE_ERROR, sam_stat); MHVTL_ERR("Data file write failure, pos: %" PRId64 ": %s", data_offset, strerror(errno)); return -1; } MHVTL_DBG(3, "Successfully wrote block: %u", blk_number); return mkEODHeader(blk_number + 1, data_offset + disk_blk_size); }