From: Mark Harvey <markh794@xxxxxxxxx> The ondisk format uses a double-linked list header with each block of data. Implement basic fixed block READ_6 & WRITE_6 OP codes. Still along way to go. - Variable READ_6 & WRITE_6 support required. - Correct sense for block read under/overrun Working enough to write small amounts of data and read it back again. e.g. tar cvf /dev/st0 /etc/passwd tar tvf /dev/st0 'blank' media created using utils/mktape mktape -m <ID> -d <type> -s <size> Where ID : Is the media filename/barcode. type : Is either 'data', 'WORM' or 'clean' to define media type size : Specify in MBytes e.g. To create a blank data media of 500 MBytes with a mediaID of ABC123 $ mktape -m ABC123 -d data -s 500 A utility to 'dump the header' information of virtual media: dump_tape -f <mediaID> e.g. $ dump_tape -f ABC123 Media : ABC123 type : Data Media serial number : ABC123_1218959649, created Sun Aug 17 17:54:09 2008 Beginning of Tape(16): Capacity 500 MB, Blk No.: 0, prev 0, curr 0, next 1152 End of Data(32): Blk No. 1, prev 0, curr 1152, next 1152, sz 0 Signed-off-by: Mark Harvey <markh794@xxxxxxxxx> Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> --- usr/bs_ssc.c | 685 ++++++++++++++++++++++++++++++++++++++++++----------- usr/bs_ssc.h | 40 +++ usr/ssc.h | 2 + usr/tgtd.h | 2 +- utils/Makefile | 34 +++ utils/dump_tape.c | 209 ++++++++++++++++ utils/mktape.c | 192 +++++++++++++++ 7 files changed, 1028 insertions(+), 136 deletions(-) create mode 100644 usr/bs_ssc.h create mode 100644 utils/Makefile create mode 100644 utils/dump_tape.c create mode 100644 utils/mktape.c diff --git a/usr/bs_ssc.c b/usr/bs_ssc.c index b2e8818..4ae3fd6 100644 --- a/usr/bs_ssc.c +++ b/usr/bs_ssc.c @@ -1,3 +1,23 @@ +/* + * SCSI stream command processing backing store + * + * Copyright (C) 2008 Mark Harvey markh794@xxxxxxxxx + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -15,163 +35,497 @@ #include "scsi.h" #include "bs_thread.h" -#define TCLP_BIT 4 -#define LONG_BIT 2 -#define BT_BIT 1 +#include "media.h" +#include "bs_ssc.h" +#include "ssc.h" -static void ssc_rdwr_request(struct scsi_cmd *cmd) +/* I'm sure there is a more efficent method then this */ +static int32_t be24_to_2comp(uint8_t *c) { - int ret, fd = cmd->dev->fd, code; + int count; + count = (c[0] << 16) | (c[1] << 8) | c[2]; + if (c[1] & 0x80) + count += (0xff << 24); + return count; +} + +static uint32_t be24_to_uint(uint8_t *c) +{ + return (c[0] << 16) | (c[1] << 8) | c[2]; +} + +static int skip_next_header(struct scsi_lu *lu) +{ + ssize_t rd; + struct ssc_info *ssc = dtype_priv(lu); + struct blk_header *h = ssc->c_blk; + + /* FIXME: Need a lock around this read */ + rd = pread(lu->fd, h, sizeof(struct blk_header), h->next); + if (rd != sizeof(struct blk_header)) + return 1; + return 0; +} + +static int skip_prev_header(struct scsi_lu *lu) +{ + ssize_t rd; + struct ssc_info *ssc = dtype_priv(lu); + struct blk_header *h = ssc->c_blk; + + /* FIXME: Need a lock around this read */ + rd = pread(lu->fd, h, sizeof(struct blk_header), h->prev); + if (rd != sizeof(struct blk_header)) + return 1; + if (h->blk_type == BLK_BOT) + return skip_next_header(lu); + return 0; +} + +static int resp_rewind(struct scsi_lu *lu) +{ + int fd; + ssize_t rd; + struct ssc_info *ssc = dtype_priv(lu); + struct blk_header *h; + + h = ssc->c_blk; + fd = lu->fd; + + eprintf("*** Backing store fd: %s %d %d ***\n", lu->path, lu->fd, fd); + + rd = pread(fd, h, sizeof(struct blk_header), 0); + if (rd < 0) + eprintf("Could not read %d bytes:%m\n", + (int)sizeof(struct blk_header)); + if (rd != sizeof(struct blk_header)) + return 1; + + return skip_next_header(lu); +} + +static int append_blk(struct scsi_cmd *cmd, uint8_t *data, + int size, int orig_sz, int type) +{ + int fd; + struct blk_header *curr; + struct blk_header *eod; + struct ssc_info *ssc; + ssize_t ret; + + ssc = dtype_priv(cmd->dev); + fd = cmd->dev->fd; + + eod = zalloc(sizeof(struct blk_header)); + if (!eod) { + eprintf("Failed to malloc %" PRId64 " bytes\n", + (uint64_t)sizeof(eod)); + return -ENOMEM; + } + + eprintf("B4 update : prev/curr/next" + " <%" PRId64 "/%" PRId64 "/%" PRId64 "> type: %d," + " num: %" PRIx64 ", ondisk sz: %d, about to write %d\n", + ssc->c_blk->prev, ssc->c_blk->curr, ssc->c_blk->next, + ssc->c_blk->blk_type, ssc->c_blk->blk_num, + ssc->c_blk->ondisk_sz, size); + + /* FIXME: Need lock protection around this */ + curr = ssc->c_blk; + curr->next = curr->curr + size + sizeof(struct blk_header); + curr->blk_type = type; + curr->ondisk_sz = size; + curr->blk_sz = orig_sz; + eod->prev = curr->curr; + eod->curr = curr->next; + eod->next = curr->next; + eod->ondisk_sz = 0; + eod->blk_sz = 0; + eod->blk_type = BLK_EOD; + eod->blk_num = curr->blk_num + 1; + eod->a = 'A'; + eod->z = 'Z'; + ssc->c_blk = eod; + /* End of protection */ + + eprintf("After update : prev/curr/next" + " <%" PRId64 "/%" PRId64 "/%" PRId64 "> type: %d," + " num: %" PRIx64 ", ondisk sz: %d\n", + curr->prev, curr->curr, curr->next, + curr->blk_type, curr->blk_num, + curr->ondisk_sz); + + eprintf("EOD blk header: prev/curr/next" + " <%" PRId64 "/%" PRId64 "/%" PRId64 "> type: %d," + " num: %" PRIx64 ", ondisk sz: %d\n", + eod->prev, eod->curr, eod->next, + eod->blk_type, eod->blk_num, + eod->ondisk_sz); + + /* Rewrite previous header with updated positioning info */ + ret = pwrite(fd, curr, sizeof(struct blk_header), (off_t)curr->curr); + if (ret != sizeof(struct blk_header)) { + eprintf("Rewrite of blk header failed: %m\n"); + sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); + goto failed_write; + } + /* Write new EOD blk header */ + ret = pwrite(fd, eod, sizeof(struct blk_header), (off_t)eod->curr); + if (ret != sizeof(struct blk_header)) { + eprintf("Write of EOD blk header failed: %m\n"); + sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); + goto failed_write; + } + + /* Write any data */ + if (size) { + ret = pwrite(fd, data, size, + (off_t)curr->curr + sizeof(struct blk_header)); + if (ret != size) { + eprintf("Write of data failed: %m\n"); + sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); + goto failed_write; + } + } + /* Write new EOD blk header */ + + free(curr); + return SAM_STAT_GOOD; + +failed_write: + free(curr); + return SAM_STAT_CHECK_CONDITION; +} + +static int prev_filemark(struct scsi_cmd *cmd) +{ + struct ssc_info *ssc = dtype_priv(cmd->dev); + + if (skip_prev_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + while (ssc->c_blk->blk_type != BLK_FILEMARK) + if (skip_prev_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + + if (ssc->c_blk->blk_type == BLK_BOT) { + skip_next_header(cmd->dev); /* Can't leave at BOT */ + sense_data_build(cmd, NO_SENSE, ASC_BOM); + return SAM_STAT_CHECK_CONDITION; + } + + return SAM_STAT_GOOD; +} + +static int next_filemark(struct scsi_cmd *cmd) +{ + struct ssc_info *ssc = dtype_priv(cmd->dev); + + if (skip_next_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + + while (ssc->c_blk->blk_type != BLK_FILEMARK) { + if (skip_next_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + + if (ssc->c_blk->blk_type == BLK_EOD) { + sense_data_build(cmd, NO_SENSE, ASC_END_OF_DATA); + return SAM_STAT_CHECK_CONDITION; + } + } + + return SAM_STAT_GOOD; +} + +static int space_filemark(struct scsi_cmd *cmd, int32_t count) +{ + dprintf("*** space %d filemark%s ***\n", count, + ((count > 1) || (count < 0)) ? "s" : ""); + while (count != 0) { + if (count > 0) { + if (next_filemark(cmd)) { + return SAM_STAT_CHECK_CONDITION; + break; + } + count--; + } else { + if (prev_filemark(cmd)) { + return SAM_STAT_CHECK_CONDITION; + break; + } + count++; + } + } + return SAM_STAT_GOOD; +} + +static int space_blocks(struct scsi_cmd *cmd, int32_t count) +{ + struct ssc_info *ssc = dtype_priv(cmd->dev); + + dprintf("*** space %d block%s ***\n", count, + ((count > 1) || (count < 0)) ? "s" : ""); + while (count != 0) { + if (count > 0) { + if (skip_next_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + if (ssc->c_blk->blk_type == BLK_EOD) { + sense_data_build(cmd, NO_SENSE, + ASC_END_OF_DATA); + return SAM_STAT_CHECK_CONDITION; + } + count--; + } else { + if (skip_prev_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + return SAM_STAT_CHECK_CONDITION; + } + if (ssc->c_blk->blk_type == BLK_BOT) { + /* Can't leave at BOT */ + skip_next_header(cmd->dev); + + sense_data_build(cmd, NO_SENSE, ASC_BOM); + return SAM_STAT_CHECK_CONDITION; + } + count++; + } + } + return SAM_STAT_GOOD; +} + +/* Return error - util written */ +static int resp_var_read(struct scsi_cmd *cmd, uint8_t *buf, uint32_t length) +{ + sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB); + return 0; +} + +static int resp_fixed_read(struct scsi_cmd *cmd, uint8_t *buf, uint32_t length) +{ + struct ssc_info *ssc; + int i, ret; + int count; + ssize_t residue; + int fd; + + count = be24_to_uint(&cmd->scb[2]); + ssc = dtype_priv(cmd->dev); + fd = cmd->dev->fd; + ret = 0; + + for (i = 0; i < count; i++) { + if (ssc->c_blk->blk_type == BLK_FILEMARK) { + eprintf("Oops - found filemark\n"); + sense_data_build(cmd, NO_SENSE, ASC_MARK); +/* FIXME: Need to update sense buffer with remaining byte count. */ + goto rd_err; + } + + if (ssc->blk_sz != ssc->c_blk->blk_sz) { + eprintf("block size mismatch %d vs %d\n", + ssc->blk_sz, ssc->c_blk->blk_sz); + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + goto rd_err; + } + + residue = pread(fd, buf, ssc->blk_sz, + ssc->c_blk->curr + sizeof(struct blk_header)); + if (ssc->blk_sz != residue) { + eprintf("Could only read %d bytes, not %d\n", + (int)residue, ssc->blk_sz); + sense_data_build(cmd, MEDIUM_ERROR, ASC_READ_ERROR); + goto rd_err; + } + ret += ssc->blk_sz; + buf += ssc->blk_sz; + + if (skip_next_header(cmd->dev)) { + eprintf("Could not read next header\n"); + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); + goto rd_err; + } + } + return ret; + +rd_err: + return 0; +} + +static void tape_rdwr_request(struct scsi_cmd *cmd) +{ + struct ssc_info *ssc; + int ret, code; uint32_t length, i, transfer_length, residue; int result = SAM_STAT_GOOD; - uint8_t buff[512]; - char *buf; - off_t rew; - uint64_t curr_pos; - uint32_t count; + uint8_t *buf; + int32_t count; + int8_t fixed; + int8_t sti; ret = 0; length = 0; i = 0; transfer_length = 0; residue = 0; - count = 0; code = 0; + ssc = dtype_priv(cmd->dev); switch (cmd->scb[0]) { case REZERO_UNIT: - rew = lseek(fd, 0, SEEK_SET); - curr_pos = lseek(fd, 0, SEEK_CUR); - if (rew) { - sense_data_build(cmd, MEDIUM_ERROR, - ASC_SEQUENTIAL_POSITION_ERR); + eprintf("**** Rewind ****\n"); + if (resp_rewind(cmd->dev)) { + sense_data_build(cmd, + MEDIUM_ERROR, ASC_SEQUENTIAL_POSITION_ERR); result = SAM_STAT_CHECK_CONDITION; } - eprintf("Rewind Successful, File Pointer at %" PRIu64",%m\n", - curr_pos); break; + case WRITE_FILEMARKS: - length = sizeof(buff); - memset(buff, 28, sizeof(buff)); - ret = write(fd, buff, length); - if (ret != length) { - sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); - result = SAM_STAT_CHECK_CONDITION; - } - eprintf("Write Filemark Successfull %d\n", ret); - curr_pos = lseek(fd, 0, SEEK_CUR); - eprintf("File Pointer at %" PRIu64",%m\n", curr_pos); + ret = be24_to_uint(&cmd->scb[2]); + eprintf("*** Write %d filemark%s ***\n", ret, + ((ret > 1) || (ret < 0)) ? "s" : ""); + + for (i = 0; i < ret; i++) + append_blk(cmd, scsi_get_out_buffer(cmd), 0, + 0, BLK_FILEMARK); + break; + case READ_6: - length = scsi_get_in_length(cmd); - ret = read(fd, scsi_get_in_buffer(cmd), length); - buf = (char *)(unsigned long)scsi_get_in_buffer(cmd); - /* buf = (char *)buf; */ - if (ret != length) { - sense_data_build(cmd, MEDIUM_ERROR, ASC_READ_ERROR); + fixed = cmd->scb[1] & 1; + sti = cmd->scb[1] & 2; + + if (fixed && sti) { + sense_data_build(cmd, ILLEGAL_REQUEST, + ASC_INVALID_FIELD_IN_CDB); result = SAM_STAT_CHECK_CONDITION; - } else { - for (i = 0; i < ret; i += 512) { - eprintf("buf[%d]=%d", i, buf[i]); - if (buf[i] == 28) { - sense_data_build(cmd, NO_SENSE, - ASC_MARK); - result = SAM_STAT_CHECK_CONDITION; - transfer_length = ((cmd->scb[2] << 16) | - (cmd->scb[3] << 8) | - (cmd->scb[4])); -/* residue = */ -/* transfer_length - i << 9; */ - residue = (length - i) << 9; - cmd->sense_buffer[3] = residue >> 24; - cmd->sense_buffer[3] = residue >> 16; - cmd->sense_buffer[3] = residue >> 8; - cmd->sense_buffer[3] = residue; - - eprintf("File Mark Detected at %d," - " Residue = %d %m\n", - i, residue); - } - } + break; } + + length = scsi_get_in_length(cmd); + count = be24_to_uint(&cmd->scb[2]); + buf = scsi_get_in_buffer(cmd); + + dprintf("*** READ_6: length %d, count %d, fixed block %s\n", + length, count, (fixed) ? "Yes" : "No"); + if (fixed) + ret = resp_fixed_read(cmd, buf, length); + else + ret = resp_var_read(cmd, buf, length); + + if (!ret) + result = SAM_STAT_CHECK_CONDITION; + eprintf("Executed READ_6, Read %d bytes\n", ret); - curr_pos = lseek(fd, 0, SEEK_CUR); - eprintf("File Pointer at %" PRIu64",%m\n", curr_pos); break; + case WRITE_6: + fixed = cmd->scb[1] & 1; + + buf = scsi_get_out_buffer(cmd); + count = be24_to_uint(&cmd->scb[2]); length = scsi_get_out_length(cmd); - ret = write(fd, scsi_get_out_buffer(cmd), length); + + if (!fixed) { /* Until supported */ + sense_data_build(cmd, ILLEGAL_REQUEST, + ASC_INVALID_FIELD_IN_CDB); + result = SAM_STAT_CHECK_CONDITION; + break; + } + + for (i = 0, ret = 0; i < count; i++) { + if (append_blk(cmd, buf, ssc->blk_sz, + ssc->blk_sz, BLK_UNCOMPRESS_DATA)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_WRITE_ERROR); + result = SAM_STAT_CHECK_CONDITION; + break; + } + buf += ssc->blk_sz; + ret += ssc->blk_sz; + } + + dprintf("*** WRITE_6 count: %d, length: %d, ret: %d, fixed: %s," + " ssc->blk_sz: %d\n", + count, length, ret, (fixed) ? "Yes" : "No", + ssc->blk_sz); + if (ret != length) { sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR); result = SAM_STAT_CHECK_CONDITION; } - eprintf("Executed WRITE_6, writen %d bytes\n", ret); - curr_pos = lseek(fd, 0, SEEK_CUR); - eprintf("File Pointer at %" PRIu64",%m\n", curr_pos); break; - case SPACE: - code = cmd->scb[1]; - count = (cmd->scb[2] << 16) | (cmd->scb[3] << 8) | - (cmd->scb[4]); - - if (code == 0) { - for (i = 0; i < count; i++) { - ret = read(fd, buff, sizeof(buff)); - if (ret != sizeof(buff)) { - sense_data_build(cmd, MEDIUM_ERROR, - ASC_SEQUENTIAL_POSITION_ERR); - result = SAM_STAT_CHECK_CONDITION; - } - curr_pos = lseek(fd, 0, SEEK_CUR); - eprintf("File Pointer at %" PRIu64",%m\n", - curr_pos); + case SPACE: + code = cmd->scb[1] & 0xf; + count = be24_to_2comp(&cmd->scb[2]); - if (buff[i*512] == 28) { - sense_data_build(cmd, NO_SENSE, - ASC_MARK); + if (code == 0) { /* Logical Blocks */ + result = space_blocks(cmd, count); + break; + } else if (code == 1) { /* Filemarks */ + result = space_filemark(cmd, count); + break; + } else if (code == 3) { /* End of data */ + while (ssc->c_blk->blk_type != BLK_EOD) + if (skip_next_header(cmd->dev)) { + sense_data_build(cmd, MEDIUM_ERROR, + ASC_MEDIUM_FORMAT_CORRUPT); result = SAM_STAT_CHECK_CONDITION; + break; } - } - } else if (code == 1) { - i = 0; - while (i < count) { - ret = read(fd, buff, sizeof(buff)); - curr_pos = lseek(fd, 0, SEEK_CUR); - eprintf("File Pointer at %" PRIu64",%m\n", - curr_pos); - if (buff[i*512] == 28) - i++; - } + } else { /* Unsupported */ + sense_data_build(cmd, ILLEGAL_REQUEST, + ASC_INVALID_FIELD_IN_CDB); + result = SAM_STAT_CHECK_CONDITION; } break; + case READ_POSITION: { - int tclp = cmd->scb[1] & TCLP_BIT; - int long_bit = cmd->scb[1] & LONG_BIT; - int bt = cmd->scb[1] & BT_BIT; - uint8_t *data; - - eprintf("Size of in_buffer = %d ", scsi_get_in_length(cmd)); - if (tclp == 1 || tclp != long_bit || - (bt == 1 && long_bit == 1)) { + int service_action = cmd->scb[1] & 0x1f; + uint8_t *data = scsi_get_in_buffer(cmd); + int len = scsi_get_in_length(cmd); + + eprintf("Size of in_buffer = %d\n", len); + eprintf("Sizeof(buf): %d\n", (int)sizeof(buf)); + eprintf("service action: 0x%02x\n", service_action); + + if (service_action == 0) { /* Short form - block ID */ + memset(data, 0, 20); + data[0] = 20; + } else if (service_action == 1) { /* Short form - vendor uniq */ + memset(data, 0, 20); + data[0] = 20; + } else if (service_action == 6) { /* Long form */ + memset(data, 0, 32); + data[0] = 32; + } else { sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB); result = SAM_STAT_CHECK_CONDITION; - } else { - memset(buff, 0, sizeof(buff)); - data = buff; - curr_pos = lseek(fd, 0, SEEK_CUR); - if (curr_pos == 0) - data[0] = 0xb4; - else - data[0] = 0x34; - memcpy(scsi_get_in_buffer(cmd), data, 20); } - break; } default: + eprintf("Unknown op code - should never see this\n"); + sense_data_build(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE); + result = SAM_STAT_CHECK_CONDITION; break; } @@ -184,56 +538,117 @@ static void ssc_rdwr_request(struct scsi_cmd *cmd) cmd, cmd->scb[0], ret, length, cmd->offset); } -static int bs_ssc_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size) +static int bs_tape_init(struct scsi_lu *lu) +{ + struct bs_thread_info *info = BS_THREAD_I(lu); + return bs_thread_open(info, tape_rdwr_request, 1); +} + +static int bs_tape_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size) { - uint64_t curr_pos; + struct ssc_info *ssc; + char *cart = NULL; + ssize_t rd; - eprintf("In bs_ssc_open\n"); + ssc = dtype_priv(lu); + + eprintf("### Enter ###\n"); *fd = backed_file_open(path, O_RDWR | O_LARGEFILE, size); if (*fd < 0) { - eprintf("Error in bs_ssc_open\n"); + eprintf("Could not open %s %m\n", path); return *fd; } - curr_pos = lseek(*fd, 0, SEEK_CUR); - eprintf("File %s File Pointer at %" PRIu64",%m\n", path, curr_pos); + eprintf("*** Backing store fd: %d ***\n", *fd); + + if (*size < (sizeof(struct blk_header) + sizeof(struct MAM))) { + eprintf("backing file too small - not correct media format\n"); + return -1; + } + if (!ssc->c_blk) + ssc->c_blk = zalloc(sizeof(struct blk_header)); + if (!ssc->c_blk) { + eprintf("malloc(%d) failed\n", (int)sizeof(struct blk_header)); + goto read_failed; + } + + /* Can't call 'resp_rewind() at this point as lu data not + * setup */ + rd = pread(*fd, ssc->c_blk, sizeof(struct blk_header), 0); + if (rd < sizeof(struct blk_header)) { + eprintf("Failed to read complete blk header: %d %m\n", (int)rd); + goto read_failed; + } + rd = pread(*fd, &ssc->mam, sizeof(struct MAM), rd); + if (rd < sizeof(struct MAM)) { + eprintf("Failed to read MAM: %d %m\n", (int)rd); + goto read_failed; + } + rd = pread(*fd, ssc->c_blk, sizeof(struct blk_header), + ssc->c_blk->next); + if (rd < sizeof(struct blk_header)) { + eprintf("Failed to read complete blk header: %d %m\n", (int)rd); + goto read_failed; + } + + switch (ssc->mam.medium_type) { + case CART_CLEAN: + cart = "Cleaning cartridge"; + break; + case CART_DATA: + cart = "data cartridge"; + break; + case CART_WORM: + cart = "WORM cartridge"; + break; + default: + cart = "Unknown cartridge type"; + break; + } + + eprintf("Media size: %d, media type: %s\n", + ssc->c_blk->blk_sz, cart); return 0; -} -static void bs_ssc_close(struct scsi_lu *lu) -{ - close(lu->fd); +read_failed: + free(ssc->c_blk); + ssc->c_blk = NULL; + return -1; } -static int bs_ssc_init(struct scsi_lu *lu) +static void bs_tape_exit(struct scsi_lu *lu) { struct bs_thread_info *info = BS_THREAD_I(lu); - return bs_thread_open(info, ssc_rdwr_request, 1); + bs_thread_close(info); } -static void bs_ssc_exit(struct scsi_lu *lu) +static void bs_tape_close(struct scsi_lu *lu) { - struct bs_thread_info *info = BS_THREAD_I(lu); - bs_thread_close(info); + struct ssc_info *ssc; + ssc = dtype_priv(lu); + free(ssc->c_blk); + ssc->c_blk = NULL; + dprintf("##### Close #####\n"); + close(lu->fd); } -static int bs_ssc_cmd_done(struct scsi_cmd *cmd) +static int bs_tape_cmd_done(struct scsi_cmd *cmd) { return 0; } -static struct backingstore_template ssc_bst = { +static struct backingstore_template tape_bst = { .bs_name = "ssc", .bs_datasize = sizeof(struct bs_thread_info), - .bs_init = bs_ssc_init, - .bs_exit = bs_ssc_exit, - .bs_open = bs_ssc_open, - .bs_close = bs_ssc_close, + .bs_init = bs_tape_init, + .bs_exit = bs_tape_exit, + .bs_open = bs_tape_open, + .bs_close = bs_tape_close, .bs_cmd_submit = bs_thread_cmd_submit, - .bs_cmd_done = bs_ssc_cmd_done, + .bs_cmd_done = bs_tape_cmd_done, }; -__attribute__((constructor)) static void bs_ssc_constructor(void) +__attribute__((constructor)) static void bs_tape_constructor(void) { - register_backingstore_template(&ssc_bst); + register_backingstore_template(&tape_bst); } diff --git a/usr/bs_ssc.h b/usr/bs_ssc.h new file mode 100644 index 0000000..7e9a7d9 --- /dev/null +++ b/usr/bs_ssc.h @@ -0,0 +1,40 @@ +/* + * structure of a 'poor mans double linked list' on disk. + */ + +/** + * Block type definitations + * + * @BLK_NOOP: No Operation.. Dummy value + * @BLK_UNCOMPRESS_DATA: If true, data block is uncompressed + * @BLK_ENCRYPTED_DATA: If true, data block is encrypted + * @BLK_FILEMARK: Represents a filemark + * @BLK_SETMARK: Represents a setmark + * @BLK_BOT: Represents a Beginning of Tape marker + * @BLK_EOD: Represents an End of Data marker + * + * Defines for types of SSC data blocks + */ +#define BLK_NOOP 0x00000000 +#define BLK_COMPRESSED_DATA 0x00000001 +#define BLK_UNCOMPRESS_DATA 0x00000002 +#define BLK_ENCRYPTED_DATA 0x00000004 +#define BLK_BOT 0x00000010 +#define BLK_EOD 0x00000020 +#define BLK_FILEMARK 0x00000040 +#define BLK_SETMARK 0x00000080 + +#define TGT_TAPE_VERSION 2 + +struct blk_header { + uint8_t a; + uint32_t ondisk_sz; + uint32_t blk_sz; + uint32_t blk_type; + uint64_t blk_num; + uint64_t prev; + uint64_t curr; + uint64_t next; + uint8_t z; +}; + diff --git a/usr/ssc.h b/usr/ssc.h index 59fef75..9b2d36f 100644 --- a/usr/ssc.h +++ b/usr/ssc.h @@ -58,6 +58,8 @@ struct ssc_info { uint64_t bytes_written; /* Bytes written this load */ struct MAM mam; + + struct blk_header *c_blk; /* Current block header */ }; #endif diff --git a/usr/tgtd.h b/usr/tgtd.h index da751c8..5406a45 100644 --- a/usr/tgtd.h +++ b/usr/tgtd.h @@ -153,7 +153,7 @@ struct scsi_lu { struct lu_phy_attr attrs; /* A pointer for each modules private use. - * Currently used by smc and mmc modules. + * Currently used by ssc, smc and mmc modules. */ void *xxc_p; }; diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000..fbb47f1 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,34 @@ +# LIBS := -L. -L.. -L../lib +INCLUDES += -I../include -I../usr + +INCLUDES += -I. +CFLAGS += -Wall -g -O2 -Wstrict-prototypes -fPIC -D_LARGEFILE64_SOURCE $(INCLUDES) + +PROGRAMS += mktape dump_tape +MKTAPE_OBJS += mktape.o +DUMP_TAPE_OBJS += dump_tape.o + +.PHONY: all +all: $(PROGRAMS) + +mktape: $(MKTAPE_OBJS) + $(CC) $^ -o $@ $(LIBS) + +-include mktape.d + +dump_tape: $(DUMP_TAPE_OBJS) + $(CC) $^ -o $@ $(LIBS) + +-include dump_tape.d + +%.o: %c + $(CC) -c $(CFLAGS) $*.c -o $*.o + @$(CC) -MM $(CFLAGS) -MF $*.d -MT $*.o $*.c + +.PHONY: install +install: $(PROGRAMS) + install -m 0755 $(PROGRAMS) $(DESTDIR)/usr/bin + +.PHONY: clean +clean: + rm -f *.o $(PROGRAMS) diff --git a/utils/dump_tape.c b/utils/dump_tape.c new file mode 100644 index 0000000..ef6b6c3 --- /dev/null +++ b/utils/dump_tape.c @@ -0,0 +1,209 @@ +/* + * Dump headers of 'tape' datafile + * + * Copyright (C) 2008 Mark Harvey markh794@xxxxxxxxx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include <time.h> +#include "scsi.h" +#include "media.h" +#include "ssc.h" +#include "bs_ssc.h" + +void print_current_header(struct blk_header *pos) +{ + if (pos->a != 'A') + printf("head sanity check failed\n"); + if (pos->z != 'Z') + printf("tail sanity check failed\n"); + + switch (pos->blk_type) { + case BLK_UNCOMPRESS_DATA: + printf(" Uncompressed data"); + break; + case BLK_FILEMARK: + printf(" Filemark"); + break; + case BLK_BOT: + printf("Beginning of Tape"); + break; + case BLK_EOD: + printf(" End of Data"); + break; + case BLK_NOOP: + printf(" No Operation"); + break; + default: + printf(" Unknown type"); + break; + } + if (pos->blk_type == BLK_BOT) + printf("(%d): Capacity %d MB, Blk No.: %" PRId64 + ", prev %" PRId64 ", curr %" PRId64 ", next %" PRId64 "\n", + pos->blk_type, + pos->blk_sz, + pos->blk_num, + (uint64_t)pos->prev, + (uint64_t)pos->curr, + (uint64_t)pos->next); + else + printf("(%d): Blk No. %" PRId64 ", prev %" PRId64 "" + ", curr %" PRId64 ", next %" PRId64 ", sz %d\n", + pos->blk_type, + pos->blk_num, + (uint64_t)pos->prev, + (uint64_t)pos->curr, + (uint64_t)pos->next, + pos->ondisk_sz); +} + +int skip_to_next_header(int fd, struct blk_header *pos) +{ + loff_t nread; + + nread = lseek64(fd, (uint64_t)pos->next, SEEK_SET); + if ((uint64_t)pos->next != nread) { + printf("Error while seeking to next header\n"); + return -1; + } + nread = read(fd, pos, sizeof(struct blk_header)); + if (nread < sizeof(struct blk_header)) { + printf("Could not read complete blk header - short read!!\n"); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int ofp; + char *progname; + char datafile[1024] = ""; + loff_t nread; + struct MAM mam; + struct blk_header current_position; + time_t t; + int a; + unsigned char *p; + + progname = argv[0]; + + if (argc < 2) { + printf("Usage: %s -f <media>\n", progname); + exit(1); + } + + while (argc > 0) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'f': + if (argc > 1) { + strncpy(datafile, argv[1], + sizeof(datafile)); + } else { + puts(" More args needed for -f\n"); + exit(1); + } + break; + } + } + argv++; + argc--; + } + + if (strlen(datafile) == 0) { + printf("Usage: %s -f <media>\n", progname); + exit(1); + } + + ofp = open(datafile, O_RDWR|O_LARGEFILE); + if (ofp == -1) { + fprintf(stderr, "%s, ", datafile); + perror("Could not open"); + exit(1); + } + nread = read(ofp, ¤t_position, sizeof(struct blk_header)); + if (nread < sizeof(current_position)) { + perror("Could not read blk header"); + exit(1); + } + nread = read(ofp, &mam, sizeof(struct MAM)); + if (nread < (sizeof(struct MAM))) { + perror("Could not read MAM"); + exit(1); + } + if (mam.tape_fmt_version != TGT_TAPE_VERSION) { + printf("Unknown media format version\n"); + exit(1); + } + + printf("Media : %s\n", mam.barcode); + switch (mam.medium_type) { + case CART_UNSPECIFIED: + printf(" type : Unspecified\n"); + break; + case CART_DATA: + printf(" type : Data\n"); + break; + case CART_CLEAN: + printf(" type : Cleaning\n"); + break; + case CART_DIAGNOSTICS: + printf(" type : Diagnostics\n"); + break; + case CART_WORM: + printf(" type : WORM\n"); + break; + case CART_MICROCODE: + printf(" type : Microcode\n"); + break; + default: + printf(" type : Unknown\n"); + } + printf("Media serial number : %s, ", mam.medium_serial_number); + + for (a = strlen((const char *)mam.medium_serial_number); a > 0; a--) + if (mam.medium_serial_number[a] == '_') + break; + if (a) { + a++; + p = &mam.medium_serial_number[a]; + t = atoll((const char *)p); + printf("created %s", ctime(&t)); + } + printf("\n"); + + print_current_header(¤t_position); + while (current_position.blk_type != BLK_EOD) { + nread = skip_to_next_header(ofp, ¤t_position); + if (nread == -1) + break; + print_current_header(¤t_position); + } + + return (0); +} diff --git a/utils/mktape.c b/utils/mktape.c new file mode 100644 index 0000000..1abf948 --- /dev/null +++ b/utils/mktape.c @@ -0,0 +1,192 @@ +/* + * Create blank media files for bs_tape backing store + * + * Copyright (C) 2008 Mark Harvey markh794@xxxxxxxxx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <syslog.h> +#include <string.h> +#include <time.h> +#include <inttypes.h> +#include "media.h" +#include "bs_ssc.h" +#include "ssc.h" + +const char *mktape_version = "0.01"; + +void usage(char *progname) +{ + + printf("Usage: %s -m barcode -s size -t type\n", progname); + printf(" Where 'size' is in Megabytes\n"); + printf(" 'type' is data | clean | WORM\n"); + printf(" 'barcode' is a string of chars\n\n"); +} + +int main(int argc, char *argv[]) +{ + int file; + struct blk_header h; + struct MAM mam; + uint8_t current_media[1024]; + long nwrite; + char *progname = argv[0]; + char *barcode = NULL; + char *media_type = NULL; + char *media_capacity = NULL; + uint32_t size; + + if (argc < 2) { + usage(progname); + exit(1); + } + + while (argc > 0) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'm': + if (argc > 1) { + barcode = argv[1]; + } else { + puts(" More args needed for -m\n"); + exit(1); + } + break; + case 's': + if (argc > 1) { + media_capacity = argv[1]; + } else { + puts(" More args needed for -s\n"); + exit(1); + } + break; + case 't': + if (argc > 1) { + media_type = argv[1]; + } else { + puts(" More args needed for -t\n"); + exit(1); + } + break; + case 'V': + printf("%s: version %s\n", + progname, mktape_version); + break; + } + } + argv++; + argc--; + } + + if (barcode == NULL) { + usage(progname); + exit(1); + } + if (media_capacity == NULL) { + usage(progname); + exit(1); + } + if (media_type == NULL) { + usage(progname); + exit(1); + } + + sscanf(media_capacity, "%d", &size); + if (size == 0) + size = 8000; + + h.a = 'A'; + h.z = 'Z'; + h.blk_type = BLK_BOT; + h.blk_num = 0; + h.blk_sz = size; + h.prev = 0; + h.curr = 0; + h.next = sizeof(mam) + sizeof(h); + + printf("blk_sz: %d, next %" PRId64 ", %" PRId64 "\n", + h.blk_sz, h.next, h.next); + printf("Sizeof(mam): %" PRId64 ", sizeof(h): %" PRId64 "\n", + (uint64_t)sizeof(mam), (uint64_t)sizeof(h)); + memset((uint8_t *)&mam, 0, sizeof(mam)); + + mam.tape_fmt_version = 2; + mam.max_capacity = size * 1048576; + mam.remaining_capacity = size * 1048576; + mam.MAM_space_remaining = sizeof(mam.vendor_unique); + mam.medium_length = 384; /* 384 tracks */ + mam.medium_width = 127; /* 127 x tenths of mm (12.7 mm) */ + memcpy(&mam.medium_manufacturer, "Foo ", 8); + memcpy(&mam.application_vendor, "Bar ", 8); + + if (!strncmp("clean", media_type, 5)) { + mam.medium_type = CART_CLEAN; + mam.medium_type_information = 20; /* Max cleaning loads */ + } else if (!strncmp("WORM", media_type, 4)) { + mam.medium_type = CART_WORM; + } else { + mam.medium_type = CART_DATA; + } + + sprintf((char *)mam.medium_serial_number, "%s_%d", + barcode, (int)time(NULL)); + sprintf((char *)mam.barcode, "%-31s", barcode); + + sprintf((char *)current_media, "%s", barcode); + syslog(LOG_DAEMON|LOG_INFO, "%s being created", current_media); + file = creat((char *)current_media, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (file == -1) { + perror("Failed creating file"); + exit(2); + } + nwrite = write(file, &h, sizeof(h)); + if (nwrite <= 0) { + perror("Unable to write header"); + exit(1); + } + nwrite = write(file, &mam, sizeof(mam)); + if (nwrite <= 0) { + perror("Unable to write MAM"); + exit(1); + } + memset(&h, 0, sizeof(h)); + h.a = 'A'; + h.z = 'Z'; + h.blk_type = BLK_EOD; + h.blk_num = 1; + h.prev = 0; + h.next = lseek64(file, 0, SEEK_CUR); + h.curr = h.next; + + nwrite = write(file, &h, sizeof(h)); + if (nwrite <= 0) { + perror("Unable to write header"); + exit(1); + } + close(file); + +exit(0); +} + -- 1.5.6.5 -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html