[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, &current_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(&current_position);
+	while (current_position.blk_type != BLK_EOD) {
+		nread = skip_to_next_header(ofp, &current_position);
+		if (nread == -1)
+			break;
+		print_current_header(&current_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