--- /home/rsharpe/open-source-devel/linux-scsi-tgt/usr/bs_ssc.c	2008-07-30 10:29:12.000000000 -0700
+++ bs_ssc.c	2008-08-17 12:27:42.000000000 -0700
@@ -14,21 +14,26 @@
 #include "tgtd.h"
 #include "scsi.h"
 #include "bs_thread.h"
+#include "ssc.h"
 
 #define TCLP_BIT            4
 #define LONG_BIT            2
 #define BT_BIT              1
 
+/*
+ * next_mark and next_rec are used in complementaty ways ...
+ */
+
 static void ssc_rdwr_request(struct scsi_cmd *cmd)
 {
 	int ret, fd = cmd->dev->fd, code;
 	uint32_t length, i, transfer_length, residue;
 	int result = SAM_STAT_GOOD;
 	uint8_t buff[512];
-	char *buf;
+	struct ssc_info *ssci = dtype_priv(cmd->dev);
 	off_t rew;
 	uint64_t curr_pos;
-	uint32_t count;
+	int32_t count;  /* This must be signed ... */
 
 	ret = 0;
 	length = 0;
@@ -47,101 +52,201 @@
 					ASC_SEQUENTIAL_POSITION_ERR);
 			result = SAM_STAT_CHECK_CONDITION;
 		}
-		eprintf("Rewind Successful, File Pointer at %" PRIu64",%m\n",
-			curr_pos);
+		ssci->next_mark = 0;
+		ssci->next_record = 0;
+		/*eprintf("Rewind Successful, File Pointer at %" PRIu64", marks = %d, records = %d, %m\n",
+			curr_pos, ssci->mark_count, ssci->rec_count);*/
 		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;
+		count = (cmd->scb[2] << 16) | (cmd->scb[3] << 8) |
+			(cmd->scb[4]);
+
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+
+		/* Now, create a filemark array if needed ... */
+		/* TODO: Make sure we allocate enough space for the count */
+		if (ssci->max_marks == ssci->mark_count) {
+			ssci->mark_offsets = (uint64_t *)realloc(ssci->mark_offsets, sizeof(uint64_t) * (20 + ssci->mark_count));
+
+			if (ssci->mark_offsets == NULL) {
+				sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR);
+				result = SAM_STAT_CHECK_CONDITION;
+				break;
+			}
+
+			ssci->max_marks += 20;
 		}
-		eprintf("Write Filemark Successfull %d\n", ret);
+		
+		for (i = 0; i < count; i++)
+			ssci->mark_offsets[ssci->next_mark++] = curr_pos;
+
+		ssci->mark_count = ssci->next_mark;
+		/*eprintf("Write Filemarks (%d) Successfull %d\n", count, ret);*/
 		curr_pos = lseek(fd, 0, SEEK_CUR);
-		eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
+		/*eprintf("WFM: File Pointer at %" PRIu64", marks %d %m\n", curr_pos, ssci->mark_count);*/
 		break;
 	case READ_6:
+		/*eprintf("READ_6: Fixed: %0X next_record %d max_records %d\n", cmd->scb[1],
+			ssci->next_record, ssci->max_records);*/
 		length = scsi_get_in_length(cmd);
+
+		/* 
+		 * Figure out if we are going to hit the next mark, and adjust
+		 * Do we need an explicit end of tape indicator as well? 
+		 * We need to check marks before checking if there is 
+		 * enough record data or too much data, because a mark
+		 * comes before a record (they can be at the same offset).
+		 * If next_mark is after our current position, we are after
+		 * the previous mark (or if there are no marks as yet).
+		 */
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		if (ssci->mark_count && ssci->next_mark < ssci->mark_count) {
+			if (curr_pos + length > ssci->mark_offsets[ssci->next_mark]) {
+				sense_data_build(cmd, 0x80 | NO_SENSE, ASC_MARK);
+				result = SAM_STAT_CHECK_CONDITION;
+				length = ssci->mark_offsets[ssci->next_mark] - curr_pos;
+				transfer_length = ((cmd->scb[2] << 16) |
+						   (cmd->scb[3] << 8) |
+						   (cmd->scb[4]));
+				/* TODO, there is an overflow possible here */
+				residue = transfer_length - length;
+				cmd->sense_buffer[3] = residue >> 24;
+				cmd->sense_buffer[3] = residue >> 16;
+				cmd->sense_buffer[3] = residue >> 8;
+				cmd->sense_buffer[3] = residue;
+				ssci->next_mark++;  /* Done with that mark */
+
+				/*eprintf("File Mark Detected at %d,"
+					" Residue = %d %m\n", i, residue);*/
+				break; /* Don't do any more ... */
+			}
+		}
+
+		/*
+		 * Also need to check if there really is a record there 
+		 */
+
+		/*
+		 * Now, check if the current record is too small, too big or
+		 * just right.
+		 */
+		if (ssci->records[ssci->next_record] != length) {
+			/*eprintf("Bad Record lenght: %d, next %d, count %d\n",
+				length, ssci->next_record, ssci->rec_count);*/
+			/* Check if we are off the end of the tape */
+			if (ssci->next_record >= ssci->rec_count)
+				sense_data_build(cmd, 0x40 | BLANK_CHECK, NO_ADDITIONAL_SENSE);
+			else
+				sense_data_build(cmd, NO_SENSE, NO_ADDITIONAL_SENSE);
+			result = SAM_STAT_CHECK_CONDITION;
+			length = ssci->records[ssci->next_record];
+			scsi_set_in_resid_by_actual(cmd, length);
+		}
+
 		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);
 			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);
-				}
-			}
 		}
-		eprintf("Executed READ_6, Read %d bytes\n", ret);
+		/*eprintf("Executed READ_6, Read %d bytes\n", ret);*/
+		ssci->next_record++;
+		/*ssci->rec_count = ssci->next_record;*/
 		curr_pos = lseek(fd, 0, SEEK_CUR);
-		eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
+		/*eprintf("R6: File Pointer at %" PRIu64", next_mark %d offset %lld %m\n", curr_pos, ssci->next_mark, ssci->mark_offsets?ssci->mark_offsets[ssci->next_mark]:NULL);*/
 		break;
 	case WRITE_6:
+		/*eprintf("WRITE_6: Fixed: %0X\n", cmd->scb[1]);*/
 		length = scsi_get_out_length(cmd);
+
+		/*
+		 * We need to keep track of the record length etc
+		 */
+		ssci->records[ssci->next_record] = length;
 		ret = write(fd, scsi_get_out_buffer(cmd), length);
 		if (ret != length) {
 			sense_data_build(cmd, MEDIUM_ERROR, ASC_WRITE_ERROR);
 			result = SAM_STAT_CHECK_CONDITION;
+		} else {
+			ssci->next_record++;
+			if (ssci->rec_count < ssci->next_record)
+				ssci->rec_count = ssci->next_record;
 		}
-		eprintf("Executed WRITE_6, writen %d bytes\n", ret);
+		/*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);
+		/*eprintf("W6: File Pointer at %" PRIu64", next record %d %m\n", curr_pos, ssci->next_record);*/
 		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);
+		/* Do we need to sign extend that number? */
+		if (cmd->scb[2] & 0x80)
+			count |= (0xFF000000);
 
-				if (buff[i*512] == 28) {
-					sense_data_build(cmd, NO_SENSE,
+		curr_pos = lseek(fd, 0, SEEK_CUR);
+		eprintf("SPACEing %d blocks, code = %0X mark_count %d next_mark %d\n", 
+			count, code, ssci->mark_count, ssci->next_mark);
+		if (code == 0) {
+			/*
+			 * this is ugly ... we should handle marks
+			 */
+			if (count < 0) {
+
+				for (i = 0; i < -count; i++)
+					curr_pos = lseek(fd, -(off_t)ssci->records[--ssci->next_record], SEEK_CUR);
+				eprintf("File Pointer at %" PRIu64", next_rec %d %m\n", curr_pos, ssci->next_record);
+				break;
+			}
+			if (ssci->mark_count && ssci->next_mark < ssci->mark_count) {
+				if (curr_pos + length > ssci->mark_offsets[ssci->next_mark]) {
+					sense_data_build(cmd, 0x80 | NO_SENSE,
 						ASC_MARK);
 					result = SAM_STAT_CHECK_CONDITION;
+					ssci->next_mark++;
 				}
 			}
+			for (i = 0; i < count; i++) {
+				/* TODO, we need to prevent stupid things */
+				curr_pos = lseek(fd, ssci->records[ssci->next_record++], SEEK_CUR);
+			}
+
+			eprintf("File Pointer at %" PRIu64",%m\n", curr_pos);
 		} else if (code == 1) {
 			i = 0;
+			if (count < 0) {
+				uint64_t offset;
+				/*
+				 * Back up to the previous file mark
+				 */
+				if (ssci->next_mark == 0)
+					offset = 0;
+				else
+					offset = ssci->mark_offsets[ssci->next_mark - 1];
+
+				ssci->next_mark--;
+				curr_pos = lseek(fd, 0, SEEK_CUR);
+				while (curr_pos != offset)
+					curr_pos = lseek(fd, -(off_t)ssci->records[--ssci->next_record], SEEK_CUR);
+				break; /* We are done here ... */
+			}
 			while (i < count) {
-				ret = read(fd, buff, sizeof(buff));
+				if (ssci->mark_count == ssci->next_mark) {
+					sense_data_build(cmd, MEDIUM_ERROR,
+						ASC_SEQUENTIAL_POSITION_ERR);
+					result = SAM_STAT_CHECK_CONDITION;
+					break;
+				}
+				/*
+				 * Space forward until we hit the file mark
+				 */
 				curr_pos = lseek(fd, 0, SEEK_CUR);
-				eprintf("File Pointer at %" PRIu64",%m\n",
-					curr_pos);
-				if (buff[i*512] == 28)
-					i++;
+				while (curr_pos < ssci->mark_offsets[ssci->next_mark])  /* Until we get there ... */
+					curr_pos = lseek(fd, ssci->records[ssci->next_record++], SEEK_CUR);
+				ssci->next_mark++; /* Skip the mark now */
+				eprintf("SFM: File Pointer at %" PRIu64", count= %d next_mark %d %m\n",
+					curr_pos, count, ssci->next_mark);
+				i++; 
 			}
 		}
 		break;
@@ -172,6 +277,7 @@
 		break;
 	}
 	default:
+		printf("Unknown command: 0x%02X ...\n", cmd->scb[0]);
 		break;
 	}
 
@@ -187,8 +293,36 @@
 static int bs_ssc_open(struct scsi_lu *lu, char *path, int *fd, uint64_t *size)
 {
 	uint64_t curr_pos;
+	char *md_file = NULL;
+	int md = 0, wc = 0;
+	struct hdr_struct {
+		uint32_t mark_count;
+		uint32_t max_records;
+		uint32_t record_count;
+	} hdr_struct;
+	struct ssc_info *ssc;
+	uint8_t *data;
 
 	eprintf("In bs_ssc_open\n");
+
+	ssc = zalloc(sizeof(struct ssc_info));
+	if (ssc)
+		dtype_priv(lu) = ssc;
+	else {
+		eprintf("Unable to allocate space for private struct\n");
+		return -ENOMEM;
+	}
+
+	ssc->blk_sz = 0; /*1 << BLK_SHIFT */
+	data = lu->mode_block_descriptor;
+
+	/* SSC devices do not need to set number of blks */
+	/* Hmmmm, see if this works ... for the density code*/
+	*(uint32_t *)(data) = 0x00000044;
+
+	/* Set default blk size */
+	*(uint32_t *)(data + 4) = __cpu_to_be32(ssc->blk_sz);
+
 	*fd = backed_file_open(path, O_RDWR | O_LARGEFILE, size);
 	if (*fd < 0) {
 		eprintf("Error in bs_ssc_open\n");
@@ -197,11 +331,154 @@
 	curr_pos = lseek(*fd, 0, SEEK_CUR);
 	eprintf("File %s File Pointer at %" PRIu64",%m\n", path, curr_pos);
 
+	/*
+	 * OK, now open our metadata file ... and init our structures
+	 * There is a header there. 12 bytes so far: Number of marks, each
+	 * mark is an 8-byte field ... then number of block length entries
+	 * each is a uint32_t ...
+	 */
+	md_file = (char *)malloc(strlen(lu->path) + 10); /* Take care below */
+	if (!md_file) {
+		eprintf("Could not allocate space for metadata file name: %m\n");
+		eprintf("File contents lost ...\n");
+	} else {
+		strcpy(md_file, lu->path);
+		strcat(md_file, ".md");
+
+		md = open(md_file, O_RDONLY, 0666);
+		if (md < 0) {
+			eprintf("Unable to open metadata file (%s): %m\n", md_file);
+			eprintf("First open or file contents lost ...\n");
+		}
+		else {
+			/*
+			 * Now, read the header, then the marks, then the
+			 * record offsets ... and close the file.
+			 * Errors, what are they?
+			 */
+			wc = read(md, (char *)&hdr_struct, sizeof(hdr_struct));
+			if (wc < 0 || wc < sizeof(hdr_struct)){
+				eprintf("Problems reading. wc = %d, %m\n", wc);
+				close(md);
+				return errno;
+			}
+
+			ssc->mark_count = hdr_struct.mark_count;
+			ssc->max_marks = hdr_struct.mark_count;
+			ssc->next_mark = 0;
+
+			ssc->max_records = hdr_struct.max_records;
+			ssc->rec_count = hdr_struct.record_count;
+
+			/* Allocate space for those marks, if any ... */
+			if (ssc->max_marks) {
+				ssc->mark_offsets = (uint64_t *)malloc(sizeof(off_t) * ssc->max_marks);
+				if (!ssc->mark_offsets) {
+					eprintf("Unable to allocate space for marks\n");
+					return ENOSPC;
+				}
+			}
+			/* Allocate space for those records ... */
+			ssc->records = (uint32_t *)malloc(sizeof(uint32_t) * hdr_struct.max_records);
+			if (!ssc->records) {
+				eprintf("Could not allocate space for records\n");
+				close(md);
+				return ENOSPC;
+			}
+
+			if (ssc->max_marks) {
+				wc = read(md, ssc->mark_offsets, ssc->mark_count * sizeof(off_t));
+				if (wc < 0 || wc < (sizeof(off_t) * ssc->mark_count)) {
+					eprintf("Problems reading. wc = %d, %m\n", wc);
+					close(md);
+					return errno;
+				}
+			}
+
+			wc = read(md, ssc->records, ssc->max_records * sizeof(uint32_t));
+			if (wc < 0) {
+				eprintf("Problems reading. wc = %d\n", wc);
+				return ENOSPC;
+			}
+			eprintf("Read %d bytes ... of record stuff\n", wc);
+
+			close(md);
+		}
+	}
+
+	if (ssc->max_records == 0) {
+		ssc->records = (uint32_t *)malloc(1000000);
+
+		ssc->max_records = 1000000; /* TODO. fix this ... */
+	}
+
 	return 0;
 }
 
 static void bs_ssc_close(struct scsi_lu *lu)
 {
+	char *md_file = NULL;
+	int md = 0, wc = 0, rem, off = 0;
+	struct hdr_struct {
+		uint32_t mark_count;
+		uint32_t max_records;
+		uint32_t record_count;
+	} hdr_struct;
+	struct ssc_info *ssci = dtype_priv(lu);
+
+	/*
+	 * Write our metadata ...
+	 */
+	md_file = (char *)malloc(strlen(lu->path) + 10); /* Take care below */
+	if (!md_file) {
+		eprintf("Could not allocate space for metadata file name: %m\n");
+		eprintf("File contents lost ...\n");
+	} else {
+		strcpy(md_file, lu->path);
+		strcat(md_file, ".md");
+
+		md = open(md_file, O_RDWR | O_TRUNC | O_CREAT, 0666);
+		if (md < 0) {
+			eprintf("Unable to open metadata file (%s) for write: %m\n", md_file);
+			eprintf("File contents lost ...\n");
+		}
+		else {
+			hdr_struct.mark_count = ssci->mark_count;
+			hdr_struct.record_count = ssci->rec_count;
+			hdr_struct.max_records = ssci->max_records;
+
+			/*
+			 * Now, write the header, then the marks, then the
+			 * record offsets ... and close the file.
+			 * Errors, what are they?
+			 */
+			wc = write(md, (char *)&hdr_struct, sizeof(hdr_struct));
+			if (wc < 0 || wc < sizeof(hdr_struct))
+				eprintf("Problems writing. wc = %d\n", wc);
+
+			if (ssci->max_marks) {
+				wc = write(md, ssci->mark_offsets, ssci->mark_count * sizeof(off_t));
+				if (wc < 0 || wc < (sizeof(off_t) * ssci->mark_count))
+					eprintf("Problems writing. wc = %d\n", wc);
+			}
+
+			off = 0;
+			rem = ssci->max_records * sizeof(uint32_t);
+			while (rem > 0) {
+				wc = write(md, ssci->records, ssci->max_records * sizeof(uint32_t));
+				if (wc < 0) {
+					eprintf("Problems writing. wc = %d\n", wc);
+					break;
+				}
+				eprintf("Wrote %d bytes ... \n", wc);
+				rem -= wc; off += wc;
+			}
+
+			close(md);
+			eprintf("Wrote the metadata ...\n");
+		}
+	}
+
 	close(lu->fd);
 }
 
