aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2014-07-25 08:03:57 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2014-07-25 08:03:57 +0000
commit3425de81164083a71e3773fd3a580ae28d394411 (patch)
treedca259bee1980266dd94079865ea92258eca66d8
parent15b98ae2e51c57f419f7f1fdcda25824206a6f54 (diff)
downloadsg3_utils-3425de81164083a71e3773fd3a580ae28d394411.tar.gz
sg_write_verify: add --repeat option logic
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@595 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog2
-rw-r--r--doc/sg_write_verify.852
-rw-r--r--src/sg_write_verify.c272
3 files changed, 225 insertions, 101 deletions
diff --git a/ChangeLog b/ChangeLog
index c3b3640d..8bb753da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.40 [20140721] [svn: r594]
+Changelog for sg3_utils-1.40 [20140724] [svn: r594]
- sg_write_verify: new utility for WRITE AND VERIFY
- sg_copy_results: correct response length calculations
- sg_format: make '-FFF' bypass mode sense/select
diff --git a/doc/sg_write_verify.8 b/doc/sg_write_verify.8
index 1df6e9a2..33571b54 100644
--- a/doc/sg_write_verify.8
+++ b/doc/sg_write_verify.8
@@ -4,9 +4,9 @@ sg_write_and_verify \- send the SCSI WRITE AND VERIFY command
.SH SYNOPSIS
.B sg_write_verify
[\fI\-\-16\fR] [\fI\-\-bytchk=BC\fR] [\fI\-\-dpo\fR] [\fI\-\-group=GN\fR]
-[\fI\-\-help\fR] [\fI\-\-in=IF\fR] [\fI\-\-ilen=ILEN\fR] \fI\-\-lba=LBA\fR
-[\fI\-\-num=NUM\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-verbose\fR]
-[\fI\-\-version\fR] [\fI\-\-wrprotect=WP\fR] \fIDEVICE\fR
+[\fI\-\-help\fR] [\fI\-\-ilen=ILEN\fR] [\fI\-\-in=IF\fR] \fI\-\-lba=LBA\fR
+[\fI\-\-num=NUM\fR] [\fI\-\-repeat\fR] [\fI\-\-timeout=TO\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wrprotect=WP\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
Send a SCSI WRITE AND VERIFY (10) or (16) command to \fIDEVICE\fR. The
@@ -30,6 +30,10 @@ block size. However if the \fIDEVICE\fR has protection information (PI)
then it becomes a bit more complicated. Hence the calculation is left to
the user with the default \fIILEN\fR, in the absence of the \fIIF\fR file,
being set to \fINUM\fR * 512.
+.PP
+For sending large amounts of data to contiguous logical blocks, a single
+WRITE AND VERIFY command may not be appropriate (e.g. due to operating
+system limitations). In such cases see the REPEAT section below.
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
The options are arranged in alphabetical order based on the long option name.
@@ -58,12 +62,6 @@ Values between 0 and 31 (inclusive) are accepted. The default is value is 0.
\fB\-h\fR, \fB\-\-help\fR
output the usage message then exit.
.TP
-\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
-read data (binary) from file named \fIIF\fR. This data will become the
-data\-out buffer and will be written to the \fIDEVICE\fR's medium. If
-\fIBC\fR is 1 then that data\-out buffer will be held until after the
-verify operation and compared to the data read back from the medium.
-.TP
\fB\-I\fR, \fB\-\-ilen\fR=\fIILEN\fR
where \fIILEN\fR is the number of bytes that will be placed in the data\-out
buffer. If the \fIIF\fR file is given then no more than \fIILEN\fR bytes
@@ -71,6 +69,13 @@ are read from that file. If the \fIIF\fR file does not contain \fIILEN\fR
bytes then an error is reported. If the \fIIF\fR file is not given then
a data\-out buffer with \fIILEN\fR bytes of 0xff is sent.
.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+read data (binary) from file named \fIIF\fR. If \fIIF\fR is "\-" then
+stdin is used. This data will become the data\-out buffer and will be written
+to the \fIDEVICE\fR's medium. If \fIBC\fR is 1 then that data\-out buffer
+will be held until after the verify operation and compared to the data read
+back from the medium.
+.TP
\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
where \fILBA\fR is the logical block address to start the write to medium.
Assumed to be in decimal unless prefixed with '0x' or has a trailing 'h'.
@@ -80,6 +85,14 @@ Must be provided.
where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to write
to the medium. The default value for \fINUM\fR is 1.
.TP
+\fB\-R\fR, \fB\-\-repeat\fR
+this option will continue to do WRITE AND VERIFY commands until the \fIIF\fR
+file is exhausted. This option requires both the \fI\-\-ilen=ILEN\fR and
+\fI\-\-in=IF\fR options to be given. Each command starts at the next logical
+block address and is for no more than \fINUM\fR blocks. The last command may
+be shorter with the number of blocks scaled as required. If there are
+residue bytes a warning is sent to stderr. See the REPEAT section.
+.TP
\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
where \fITO\fR is the command timeout value in seconds. The default value is
60 seconds. If \fINUM\fR is large then command may require considerably more
@@ -95,6 +108,27 @@ output version string then exit.
set the WRPROTECT field in the cdb to \fIWP\fR. The default value is 0 which
implies no protection information is sent (along with the user data) in the
data\-out buffer.
+.SH REPEAT
+For data sizes around a megabyte and larger, it may be appropraite to send
+multiple SCSI WRITE AND VERIFY commands due to operating system
+limitations (e.g. pass\-through SCSI interfaces often limit the amount
+of data that can be passed with a SCSI command). With this utility the
+mechanism for doing that is the \fI\-\-repeat\fR option.
+.PP
+In this mode the \fI\-\-ilen=ILEN\fR and \fI\-\-in=IF\fR options must be
+given. The \fIILEN\fR and \fINUM\fR values are treated as a per SCSI command
+parameters. Up to \fIILEN\fR bytes will be read from the \fIIF\fR file
+continually until it is exhausted. If the \fIIF\fR file is stdin, reading
+continues until an EOF is detected. The data read from each iteration becomes
+the data\-out buffer for a new WRITE AND VERIFY command.
+.PP
+The last read from the file (or stdin) may read less than \fIILEN\fR bytes
+in which case the number of logical blocks sent to the last WRITE AND VERIFY
+is scaled back accordingly. If there is a residual number of bytes left
+after that scaling then that is reported to stderr.
+.PP
+If an error occurs then that is reported to stderr and via the exit status
+and the utility stops at that point.
.SH NOTES
Other SCSI WRITE commands have a Force Unit Access (FUA) bit but that is
set (implicitly) by WRITE AND VERIFY commands hence there is no option to set
diff --git a/src/sg_write_verify.c b/src/sg_write_verify.c
index 8ebb2751..cb5d1e9d 100644
--- a/src/sg_write_verify.c
+++ b/src/sg_write_verify.c
@@ -33,7 +33,7 @@
#include "sg_pt.h"
#include "sg_cmds_basic.h"
-static const char * version_str = "1.02 20140721";
+static const char * version_str = "1.02 20140723";
#define ME "sg_write_verify: "
@@ -50,16 +50,27 @@ static const char * version_str = "1.02 20140721";
#define DEF_TIMEOUT_SECS 60
+/* Only uncomment the following for testing */
+#define WRITE_FOR_WVERIFY 1
+
+#ifdef WRITE_FOR_WVERIFY
+#define WRITE10_CMD 0x2a
+#define WRITE16_CMD 0x8a
+
+#warning "<<<TEST>>> version using WRITE cdbs instead of WRITE AND VERIFY"
+#endif
+
static struct option long_options[] = {
{"16", no_argument, 0, 'S'},
{"bytchk", required_argument, 0, 'b'},
{"dpo", no_argument, 0, 'd'},
{"group", required_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
- {"in", required_argument, 0, 'i'},
{"ilen", required_argument, 0, 'I'},
+ {"in", required_argument, 0, 'i'},
{"lba", required_argument, 0, 'l'},
{"num", required_argument, 0, 'n'},
+ {"repeat", no_argument, 0, 'R'},
{"timeout", required_argument, 0, 't'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
@@ -74,9 +85,10 @@ usage()
fprintf(stderr, "Usage: "
"sg_write_verify [--16] [--bytchk=BC] [--dpo] [--group=GN] "
"[--help]\n"
- " [--in=IF] [--ilen=IL] --lba=LBA "
+ " [--ilen=IL] [--in=IF] --lba=LBA "
"[--num=NUM]\n"
- " [--timeout=TO] [--verbose] [--version]\n"
+ " [--repeat] [--timeout=TO] [--verbose] "
+ "[--version]\n"
" [--wrprotect=WPR] DEVICE\n"
" where:\n"
" --16|-S do WRITE AND VERIFY(16) (default: 10)\n"
@@ -84,28 +96,30 @@ usage()
" --dpo|-d set DPO bit (default: 0)\n"
" --group=GN|-g GN GN is group number (default: 0)\n"
" --help|-h print out usage message\n"
- " --in=IF|-i IF IF is a file containing the data to "
- "be written\n"
" --ilen=IL| -I IL input (file) length in bytes, becomes "
"data-out\n"
" buffer length (def: deduced from IF "
"size)\n"
+ " --in=IF|-i IF IF is a file containing the data to "
+ "be written\n"
" --lba=LBA|-l LBA LBA of the first block to write "
"and verify;\n"
" no default, must be given\n"
" --num=NUM|-n NUM number of logical blocks to write and "
"verify\n"
+ " --repeat|-R while IF still has data to read, send "
+ "another\n"
+ " command, bumping LBA with up to NUM "
+ "blocks again\n"
" --timeout=TO|-t TO command timeout in seconds (def: 60)\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string then exit\n"
" --wrprotect|-w WPR WPR is the WRPROTECT field value "
"(def: 0)\n\n"
- "Performs a SCSI WRITE AND VERIFY (10 or 16) command. NUM logical "
- "blocks\nstarting at LBA written to DEVICE. Written data is fetched "
- "from the IF file\n(of no more than IL bytes). If BC is 0 then a "
- "verify takes place (e.g. ECC\nrechecked) after the write. If BC "
- "is 1 then additionally data is read back\nand compared with the "
- "original data-out buffer (that the DEVICE received).\n"
+ "Performs a SCSI WRITE AND VERIFY (10 or 16) command on DEVICE, "
+ "startings\nat LBA for NUM logical blocks. More commands performed "
+ "only if '--repeat'\noption given. Data to be written is fetched "
+ "from the IF file.\n"
);
}
@@ -121,7 +135,11 @@ run_scsi_transaction(int sg_fd, const unsigned char *cdbp, int cdb_len,
struct sg_pt_base * ptvp;
char b[32];
+#ifdef WRITE_FOR_WVERIFY
+ snprintf(b, sizeof(b), "Write(%d)", cdb_len);
+#else
snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len);
+#endif
if (verbose) {
fprintf(stderr, " %s cmd: ", b);
for (k = 0; k < cdb_len; ++k)
@@ -188,7 +206,11 @@ sg_ll_write_verify10(int sg_fd, int wrprotect, int dpo, int bytchk,
unsigned char wv_cdb[WRITE_VERIFY10_CMDLEN];
memset(wv_cdb, 0, WRITE_VERIFY10_CMDLEN);
+#ifdef WRITE_FOR_WVERIFY
+ wv_cdb[0] = WRITE10_CMD;
+#else
wv_cdb[0] = WRITE_VERIFY10_CMD;
+#endif
wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
if (dpo)
wv_cdb[1] |= 0x10;
@@ -219,7 +241,11 @@ sg_ll_write_verify16(int sg_fd, int wrprotect, int dpo, int bytchk,
memset(wv_cdb, 0, sizeof(wv_cdb));
+#ifdef WRITE_FOR_WVERIFY
+ wv_cdb[0] = WRITE16_CMD;
+#else
wv_cdb[0] = WRITE_VERIFY16_CMD;
+#endif
wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
if (dpo)
wv_cdb[1] |= 0x10;
@@ -269,7 +295,7 @@ open_if(const char * fn, int got_stdin)
int
main(int argc, char * argv[])
{
- int sg_fd, res, c, n;
+ int sg_fd, res, c, n, first_time;
unsigned char * wvb = NULL;
void * wrkBuff = NULL;
int dpo = 0;
@@ -280,6 +306,8 @@ main(int argc, char * argv[])
uint64_t llba = 0;
int lba_given = 0;
uint32_t num_lb = 1;
+ uint32_t snum_lb = 1;
+ int repeat = 0;
int timeout = DEF_TIMEOUT_SECS;
int verbose = 0;
int64_t ll;
@@ -288,14 +316,15 @@ main(int argc, char * argv[])
const char * ifnp;
int has_filename = 0;
int ilen = -1;
- int wfd = -1;
+ int ifd = -1;
int ret = 1;
+ int b_p_lb = 512;
char cmd_name[32];
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "b:dg:hi:I:l:n:St:w:vV", long_options,
+ c = getopt_long(argc, argv, "b:dg:hi:I:l:n:RSt:w:vV", long_options,
&option_index);
if (c == -1)
break;
@@ -358,6 +387,9 @@ main(int argc, char * argv[])
}
num_lb = (uint32_t)n;
break;
+ case 'R':
+ ++repeat;
+ break;
case 'S':
do_16 = 1;
given_do_16 = 1;
@@ -414,6 +446,26 @@ main(int argc, char * argv[])
usage();
return SG_LIB_SYNTAX_ERROR;
}
+ if (repeat) {
+ if (! has_filename) {
+ fprintf(stderr, "with '--repeat' need '--in=IF' option\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (ilen < 1) {
+ fprintf(stderr, "with '--repeat' need '--ilen=ILEN' option\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else {
+ b_p_lb = ilen / num_lb;
+ if (b_p_lb < 64) {
+ fprintf(stderr, "calculated %d bytes per logical block, "
+ "too small\n", b_p_lb);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ }
sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
if (sg_fd < 0) {
@@ -432,90 +484,128 @@ main(int argc, char * argv[])
fprintf(stderr, "Switching to %s because LBA or NUM too large\n",
cmd_name);
- //If a file with data to write has been provided
- if (has_filename) {
- struct stat a_stat;
-
- if ((1 == strlen(ifnp)) && ('-' == ifnp[0])) {
- fprintf(stderr, ME "don't allow stdin for write "
- "file\n");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- wfd = open_if(ifnp, 0);
- if (wfd < 0) {
- ret = -wfd;
- goto err_out;
- }
- if (ilen < 1) {
- if (fstat(wfd, &a_stat) < 0) {
- fprintf(stderr, "Could not fstat(%s)\n", ifnp);
- goto err_out;
- }
- ilen = (int)a_stat.st_size;
- if (ilen < 1) {
- fprintf(stderr, "%s file size too small\n", ifnp);
- goto err_out;
- } else if (verbose)
- fprintf(stderr, "Using file size of %d bytes\n", ilen);
- }
- if (NULL == (wrkBuff = malloc(ilen))) {
- fprintf(stderr, ME "out of memory\n");
- ret = SG_LIB_CAT_OTHER;
- goto err_out;
- }
- wvb = (unsigned char *)wrkBuff;
-
- res = read(wfd, wvb, ilen);
- if (res < 0) {
- fprintf(stderr, "Could not read from %s", ifnp);
- goto err_out;
- }
-
- //Check if the amount to write is not bigger than file itself
- if (res < ilen) {
- fprintf(stderr, "Read only %d bytes (expected %d) from %s\n",
- res, ilen, ifnp);
- goto err_out;
- }
- } else {
- if (ilen < 1) {
- if (verbose)
- fprintf(stderr, "Default write length to %d*%d=%d bytes\n",
- num_lb, 512, 512 * num_lb);
- ilen = 512 * num_lb;
- }
- if (NULL == (wrkBuff = malloc(ilen))) {
- fprintf(stderr, ME "out of memory\n");
- ret = SG_LIB_CAT_OTHER;
- goto err_out;
- }
- wvb = (unsigned char *)wrkBuff;
- /* Not sure this is a good idea to default contents to 0xff bytes */
- memset(wrkBuff, 0xff, ilen);
- }
-
if (verbose) {
fprintf(stderr, "Issue %s to device %s\n\tilen= %d "
"(0x%x), lba=%" PRIu64 " (0x%" PRIx64 ")\n\twrprotect=%d, "
- "dpo=%d, bytchk=%d, group=%d\n", cmd_name, device_name,
- ilen, ilen, llba, llba, wrprotect, dpo, bytchk, group);
+ "dpo=%d, bytchk=%d, group=%d, repeat=%d\n", cmd_name,
+ device_name, ilen, ilen, llba, llba, wrprotect, dpo, bytchk,
+ group, repeat);
}
- if (do_16)
- res = sg_ll_write_verify16(sg_fd, wrprotect, dpo, bytchk, llba,
- num_lb, group, wvb, ilen, timeout,
- verbose);
- else
- res = sg_ll_write_verify10(sg_fd, wrprotect, dpo, bytchk,
- (unsigned int)llba, num_lb, group, wvb,
- ilen, timeout, verbose);
- ret = res;
+ first_time = 1;
+ do {
+ if (first_time) {
+ //If a file with data to write has been provided
+ if (has_filename) {
+ struct stat a_stat;
+
+ if ((1 == strlen(ifnp)) && ('-' == ifnp[0])) {
+ ifd = STDIN_FILENO;
+ ifnp = "<stdin>";
+ if (verbose > 1)
+ fprintf(stderr, "Reading input data from stdin\n");
+ } else {
+ ifd = open_if(ifnp, 0);
+ if (ifd < 0) {
+ ret = -ifd;
+ goto err_out;
+ }
+ }
+ if (ilen < 1) {
+ if (fstat(ifd, &a_stat) < 0) {
+ fprintf(stderr, "Could not fstat(%s)\n", ifnp);
+ goto err_out;
+ }
+ if (! S_ISREG(a_stat.st_mode)) {
+ fprintf(stderr, "Cannot determine IF size, please "
+ "give '--ilen='\n");
+ goto err_out;
+ }
+ ilen = (int)a_stat.st_size;
+ if (ilen < 1) {
+ fprintf(stderr, "%s file size too small\n", ifnp);
+ goto err_out;
+ } else if (verbose)
+ fprintf(stderr, "Using file size of %d bytes\n", ilen);
+ }
+ if (NULL == (wrkBuff = malloc(ilen))) {
+ fprintf(stderr, ME "out of memory\n");
+ ret = SG_LIB_CAT_OTHER;
+ goto err_out;
+ }
+ wvb = (unsigned char *)wrkBuff;
+ res = read(ifd, wvb, ilen);
+ if (res < 0) {
+ fprintf(stderr, "Could not read from %s", ifnp);
+ goto err_out;
+ }
+ if (res < ilen) {
+ fprintf(stderr, "Read only %d bytes (expected %d) from "
+ "%s\n", res, ilen, ifnp);
+ if (repeat)
+ fprintf(stderr, "Will scale subsequent pieces when "
+ "repeat=1, but this is first\n");
+ goto err_out;
+ }
+ } else {
+ if (ilen < 1) {
+ if (verbose)
+ fprintf(stderr, "Default write length to %d*%d=%d "
+ "bytes\n", num_lb, 512, 512 * num_lb);
+ ilen = 512 * num_lb;
+ }
+ if (NULL == (wrkBuff = malloc(ilen))) {
+ fprintf(stderr, ME "out of memory\n");
+ ret = SG_LIB_CAT_OTHER;
+ goto err_out;
+ }
+ wvb = (unsigned char *)wrkBuff;
+ /* Not sure about this: default contents to 0xff bytes */
+ memset(wrkBuff, 0xff, ilen);
+ }
+ first_time = 0;
+ snum_lb = num_lb;
+ } else { /* repeat=1, first_time=0, must be reading file */
+ llba += snum_lb;
+ res = read(ifd, wvb, ilen);
+ if (res < 0) {
+ fprintf(stderr, "Could not read from %s", ifnp);
+ goto err_out;
+ } else {
+ if (verbose > 1)
+ fprintf(stderr, "Subsequent read from %s got %d bytes\n",
+ ifnp, res);
+ if (0 == res)
+ break;
+ if (res < ilen) {
+ snum_lb = (uint32_t)(res / b_p_lb);
+ n = res % b_p_lb;
+ if (0 != n)
+ fprintf(stderr, ">>> warning: ignoring last %d "
+ "bytes of %s\n", n, ifnp);
+ if (snum_lb < 1)
+ break;
+ }
+ }
+ }
+ if (do_16)
+ res = sg_ll_write_verify16(sg_fd, wrprotect, dpo, bytchk, llba,
+ snum_lb, group, wvb, ilen, timeout,
+ verbose);
+ else
+ res = sg_ll_write_verify10(sg_fd, wrprotect, dpo, bytchk,
+ (unsigned int)llba, snum_lb, group,
+ wvb, ilen, timeout, verbose);
+ ret = res;
+ if (ret || (snum_lb != num_lb))
+ break;
+ } while (repeat);
+
err_out:
if (wrkBuff)
free(wrkBuff);
- if (wfd >= 0)
- close(wfd);
+ if ((ifd >= 0) && (STDIN_FILENO != ifd))
+ close(ifd);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
fprintf(stderr, "close error: %s\n", safe_strerror(-res));