>From ea4d57fca07516c03980970cf90b937c45e3811e Mon Sep 17 00:00:00 2001 From: Mark Harvey <markh794 at gmail.com> Date: Mon, 18 Aug 2008 15:03:21 +1000 Subject: New backing store for ssc type devices. 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. - Race condition on blk header between threads. - 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 at gmail.com> --- usr/Makefile | 3 +- usr/bs_tape.c | 634 +++++++++++++++++++++++++++++++++++++++++++++++++++++ usr/bs_tape.h | 40 ++++ usr/ssc.h | 2 + usr/tgtd.h | 2 +- utils/Makefile | 34 +++ utils/dump_tape.c | 209 ++++++++++++++++++ utils/mktape.c | 192 ++++++++++++++++ 8 files changed, 1114 insertions(+), 2 deletions(-) diff --git a/usr/Makefile b/usr/Makefile index 4245709..aaeb227 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -57,7 +57,8 @@ LIBS += -lpthread PROGRAMS += tgtd tgtadm SCRIPTS += ../scripts/tgt-setup-lun TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \ - parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o bs.o + parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o \ + bs_tape.o bs.o MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-setup-lun.8 TGTD_DEP = $(TGTD_OBJS:.o=.d) diff --git a/usr/bs_tape.c b/usr/bs_tape.c new file mode 100644 index 0000000..227588a --- /dev/null +++ b/usr/bs_tape.c @@ -0,0 +1,634 @@ +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/fs.h> +#include <sys/epoll.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" +#include "scsi.h" +#include "bs_thread.h" + +#include "media.h" +#include "bs_tape.h" +#include "ssc.h" + +/* I'm sure there is a more efficent method then this */ +static int32_t be24_to_2comp(uint8_t *c) +{ + 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 *buf; + int32_t count; + int8_t fixed; + int8_t sti; + + ret = 0; + length = 0; + i = 0; + transfer_length = 0; + residue = 0; + code = 0; + ssc = dtype_priv(cmd->dev); + + switch (cmd->scb[0]) { + case REZERO_UNIT: + eprintf("**** Rewind ****\n"); + if (resp_rewind(cmd->dev)) { + sense_data_build(cmd, + MEDIUM_ERROR, ASC_SEQUENTIAL_POSITION_ERR); + result = SAM_STAT_CHECK_CONDITION; + } + break; + + case WRITE_FILEMARKS: + 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: + 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; + 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); + 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); + + 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; + } + break; + + case SPACE: + code = cmd->scb[1] & 0xf; + count = be24_to_2comp(&cmd->scb[2]); + + 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 { /* Unsupported */ + sense_data_build(cmd, ILLEGAL_REQUEST, + ASC_INVALID_FIELD_IN_CDB); + result = SAM_STAT_CHECK_CONDITION; + } + break; + + case READ_POSITION: + { + 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; + } + 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; + } + + dprintf("io done %p %x %d %u\n", cmd, cmd->scb[0], ret, length); + + scsi_set_result(cmd, result); + + if (result != SAM_STAT_GOOD) + eprintf("io error %p %x %d %d %" PRIu64 ", %m\n", + cmd, cmd->scb[0], ret, length, cmd->offset); +} + +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); +} + +static int bs_tape_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size) +{ + struct ssc_info *ssc; + char *cart = NULL; + ssize_t rd; + + ssc = dtype_priv(lu); + + eprintf("### Enter ###\n"); + *fd = backed_file_open(path, O_RDWR | O_LARGEFILE, size); + if (*fd < 0) { + eprintf("Could not open %s %m\n", path); + return *fd; + } + 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; + +read_failed: + free(ssc->c_blk); + ssc->c_blk = NULL; + return -1; +} + +static void bs_tape_exit(struct scsi_lu *lu) +{ + struct bs_thread_info *info = BS_THREAD_I(lu); + bs_thread_close(info); +} + +static void bs_tape_close(struct scsi_lu *lu) +{ + 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_tape_cmd_done(struct scsi_cmd *cmd) +{ + return 0; +} + +static struct backingstore_template tape_bst = { + .bs_name = "tape", + .bs_datasize = sizeof(struct bs_thread_info), + .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_tape_cmd_done, +}; + +__attribute__((constructor)) static void bs_tape_constructor(void) +{ + register_backingstore_template(&tape_bst); +} diff --git a/usr/bs_tape.h b/usr/bs_tape.h new file mode 100644 index 0000000..7e9a7d9 --- /dev/null +++ b/usr/bs_tape.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 4febcd3..be5d9c0 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..6901c7c --- /dev/null +++ b/utils/dump_tape.c @@ -0,0 +1,209 @@ +/* + * Dump headers of 'tape' datafile + * + * Copyright (C) 2008 Mark Harvey markh794 at gmail.com + * + * 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_tape.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..f9f6ae5 --- /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 at gmail.com + * + * 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_tape.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.4.3 -- To unsubscribe from this list: send the line "unsubscribe stgt" in the body of a message to majordomo at vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html |