[stgt] [PATCH 1/2] DLL backing store
Mark Harvey
markh794 at gmail.com
Mon Aug 18 07:48:48 CEST 2008
>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
More information about the stgt
mailing list