1
#define _XOPEN_SOURCE 500
10
#define __STDC_FORMAT_MACROS 1
18
#include "sg_cmds_basic.h"
21
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
23
* Copyright (c) 2012-2013, Kaminario Technologies LTD
24
* All rights reserved.
25
* Redistribution and use in source and binary forms, with or without
26
* modification, are permitted provided that the following conditions are met:
27
* * Redistributions of source code must retain the above copyright
28
* notice, this list of conditions and the following disclaimer.
29
* * Redistributions in binary form must reproduce the above copyright
30
* notice, this list of conditions and the following disclaimer in the
31
* documentation and/or other materials provided with the distribution.
32
* * Neither the name of the <organization> nor the
33
* names of its contributors may be used to endorse or promote products
34
* derived from this software without specific prior written permission.
36
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
37
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
40
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
45
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47
* This command performs a SCSI COMPARE AND WRITE on the given lba.
51
static const char * version_str = "1.04 20130516";
53
#define DEF_BLOCK_SIZE 512
54
#define DEF_NUM_BLOCKS (1)
55
#define DEF_BLOCKS_PER_TRANSFER 8
56
#define DEF_TIMEOUT_SECS 60
58
#define COMPARE_AND_WRITE_OPCODE (0x89)
59
#define COMPARE_AND_WRITE_CDB_SIZE (16)
61
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
63
#define ME "sg_compare_and_write: "
65
static struct option long_options[] = {
66
{"dpo", no_argument, 0, 'd'},
67
{"fua", no_argument, 0, 'f'},
68
{"fua_nv", no_argument, 0, 'F'},
69
{"group", required_argument, 0, 'g'},
70
{"help", no_argument, 0, 'h'},
71
{"in", required_argument, 0, 'i'},
72
{"lba", required_argument, 0, 'l'},
73
{"num", required_argument, 0, 'n'},
74
{"timeout", required_argument, 0, 't'},
75
{"verbose", no_argument, 0, 'v'},
76
{"version", no_argument, 0, 'V'},
77
{"wrprotect", required_argument, 0, 'w'},
78
{"xferlen", required_argument, 0, 'x'},
97
const char * device_name;
98
struct caw_flags flags;
104
fprintf(stderr, "Usage: "
105
"sg_compare_and_write [--dpo] [--fua] [--fua_nv] "
106
"[--group=GN] [--help]\n"
107
" --in=IF --lba=LBA [--num=NUM] "
109
" [--verbose] [--version] "
111
" [--xferlen=LEN] DEVICE\n"
113
" --dpo|-d set the dpo bit in cdb (def: "
115
" --fua|-f set the fua bit in cdb (def: "
117
" --fua_nv|-F set the fua_nv bit in cdb (def: "
119
" --group=GN|-g GN GN is GROUP NUMBER to set in "
121
" --help|-h print out usage message\n"
122
" --in=IF|-i IF IF is input file, read the compare "
125
" --lba=LBA|-l LBA LBA of the first block of the "
126
"compare and write\n"
127
" --num=NUM|-n NUM number of blocks to "
128
"compare/write (def: 1)\n"
129
" --timeout=TO|-t TO timeout for the command "
131
" --verbose|-v increase verbosity (use '-vv' for "
133
" --version|-V print version string then exit\n"
134
" --wrprotect=WP|-w WP write protect information "
136
" --xferlen=LEN|-x LEN number of bytes to transfer "
139
"(NUM * default_block_size * 2)\n"
141
"Performs a SCSI COMPARE AND WRITE operation.\n");
145
parse_args(int argc, char* argv[])
152
memset(&opts, 0, sizeof(opts));
153
opts.numblocks = DEF_NUM_BLOCKS;
154
/* COMPARE AND WRITE defines 2*buffers compare + write */
155
opts.xfer_len = 2*DEF_NUM_BLOCKS*DEF_BLOCK_SIZE;
156
opts.timeout = DEF_TIMEOUT_SECS;
157
opts.device_name = NULL;
159
int option_index = 0;
161
c = getopt_long(argc, argv, "dfFg:hi:l:n:t:vVw:x:",
162
long_options, &option_index);
171
opts.flags.fua_nv = 1;
177
opts.flags.group = sg_get_num(optarg);
178
if ((opts.flags.group < 0) ||
179
(opts.flags.group > 31)) {
180
fprintf(stderr, "argument to '--group' "
181
"expected to be 0 to 31\n");
182
goto out_err_no_usage;
190
strncpy(opts.ifilename, optarg,
191
sizeof(opts.ifilename));
195
ll = sg_get_llnum(optarg);
197
fprintf(stderr, "bad argument to '--lba'\n");
198
goto out_err_no_usage;
200
opts.lba = (uint64_t)ll;
204
opts.numblocks = sg_get_num(optarg);
205
if (opts.numblocks < 0) {
206
fprintf(stderr, "bad argument to '--num'\n");
207
goto out_err_no_usage;
211
opts.timeout = sg_get_num(optarg);
212
if (opts.timeout < 0) {
213
fprintf(stderr, "bad argument to "
215
goto out_err_no_usage;
222
fprintf(stderr, ME "version: %s\n", version_str);
225
opts.flags.wrprotect = sg_get_num(optarg);
226
if (opts.flags.wrprotect >> 3) {
227
fprintf(stderr, "bad argument to "
228
"'--wrprotect' not in range 0-7\n");
229
goto out_err_no_usage;
233
opts.xfer_len = sg_get_num(optarg);
234
if (opts.xfer_len < 0) {
235
fprintf(stderr, "bad argument to "
237
goto out_err_no_usage;
241
fprintf(stderr, "unrecognised option code 0x%x ??\n",
247
if (NULL == opts.device_name) {
248
opts.device_name = argv[optind];
252
for (; optind < argc; ++optind)
253
fprintf(stderr, "Unexpected extra argument: "
254
"%s\n", argv[optind]);
258
if (NULL == opts.device_name) {
259
fprintf(stderr, "missing device name!\n");
263
fprintf(stderr, "missing input file\n");
267
fprintf(stderr, "missing lba\n");
279
#define FLAG_FUA (0x8)
280
#define FLAG_FUA_NV (0x2)
281
#define FLAG_DPO (0x10)
282
#define WRPROTECT_MASK (0x7)
283
#define WRPROTECT_SHIFT (5)
286
sg_build_scsi_cdb(unsigned char * cdbp, unsigned int blocks,
287
int64_t start_block, struct caw_flags flags)
289
memset(cdbp, 0, COMPARE_AND_WRITE_CDB_SIZE);
290
cdbp[0] = COMPARE_AND_WRITE_OPCODE;
291
cdbp[1] = (flags.wrprotect && WRPROTECT_MASK) << WRPROTECT_SHIFT;
297
cdbp[1] |= FLAG_FUA_NV;
298
cdbp[2] = (unsigned char)((start_block >> 56) & 0xff);
299
cdbp[3] = (unsigned char)((start_block >> 48) & 0xff);
300
cdbp[4] = (unsigned char)((start_block >> 40) & 0xff);
301
cdbp[5] = (unsigned char)((start_block >> 32) & 0xff);
302
cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
303
cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
304
cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
305
cdbp[9] = (unsigned char)(start_block & 0xff);
306
/* cdbp[10-12] are reserved */
307
cdbp[13] = (unsigned char)(blocks & 0xff);
308
cdbp[14] = (unsigned char)(flags.group & 0x1f);
313
sg_compare_and_write(int sg_fd, unsigned char * buff, int blocks,
314
int64_t lba, int xfer_len, struct caw_flags flags,
318
unsigned char cawCmd[COMPARE_AND_WRITE_CDB_SIZE];
319
unsigned char senseBuff[SENSE_BUFF_LEN];
320
struct sg_pt_base * ptvp;
323
if (sg_build_scsi_cdb(cawCmd, blocks, lba, flags)) {
324
fprintf(stderr, ME "bad cdb build, lba=0x%"PRIx64", "
325
"blocks=%d\n", lba, blocks);
328
ptvp = construct_scsi_pt_obj();
330
fprintf(sg_warnings_strm, "Could not construct scsit_pt_obj, "
331
"out of " "memory\n");
335
set_scsi_pt_cdb(ptvp, cawCmd, COMPARE_AND_WRITE_CDB_SIZE);
336
set_scsi_pt_sense(ptvp, senseBuff, sizeof(senseBuff));
337
set_scsi_pt_data_out(ptvp, buff, xfer_len);
339
fprintf(stderr, " Compare and write cdb: ");
340
for (k = 0; k < COMPARE_AND_WRITE_CDB_SIZE; ++k)
341
fprintf(stderr, "%02x ", cawCmd[k]);
342
fprintf(stderr, "\n");
344
if ((verbose > 2) && (xfer_len > 0)) {
345
fprintf(stderr, " Data-out buffer contents:\n");
346
dStrHex((const char *)buff, xfer_len, 1);
348
res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT_SECS, verbose);
349
ret = sg_cmds_process_resp(ptvp, "COMPARE AND WRITE", res, 0,
350
senseBuff, 1 /* noisy */, verbose,
354
else if (-2 == ret) {
356
case SG_LIB_CAT_NOT_READY:
357
case SG_LIB_CAT_INVALID_OP:
358
case SG_LIB_CAT_UNIT_ATTENTION:
359
case SG_LIB_CAT_ILLEGAL_REQ:
360
case SG_LIB_CAT_ABORTED_COMMAND:
363
case SG_LIB_CAT_RECOVERED:
364
case SG_LIB_CAT_NO_SENSE:
367
case SG_LIB_CAT_MEDIUM_HARD:
372
slen = get_scsi_pt_sense_len(ptvp);
373
valid = sg_get_sense_info_fld(senseBuff, slen,
376
fprintf(stderr, "Medium or hardware "
377
"error starting at lba=%"
378
PRIu64" [0x%"PRIx64"]\n", ull,
390
destruct_scsi_pt_obj(ptvp);
396
open_if(const char * inf)
398
int fd = open(inf, O_RDONLY);
400
fprintf(stderr, ME "open error: %s: %s\n", inf,
402
return -1*SG_LIB_FILE_ERROR;
408
open_of(const char * outf, int verbose)
410
int sg_fd = sg_cmds_open_device(outf, 0 /* rw */, verbose);
412
fprintf(stderr, ME "open error: %s: %s\n", outf,
413
safe_strerror(-sg_fd));
414
return -1*SG_LIB_FILE_ERROR;
424
main(int argc, char * argv[])
429
unsigned char * wrkBuff = NULL;
431
res = parse_args(argc, argv);
433
fprintf(stderr, "Failed parsing args\n");
438
fprintf(stderr, "Running COMPARE AND WRITE command with the "
439
"following options:\n in=%s device=%s lba=0x%"
440
PRIx64 " num_blocks=%d xfer_len=%d timeout=%d\n",
441
opts.ifilename, opts.device_name, opts.lba,
442
opts.numblocks, opts.xfer_len, opts.timeout);
444
infd = open_if(opts.ifilename);
450
outfd = open_of(opts.device_name, opts.verbose);
456
wrkBuff = (unsigned char *)malloc(opts.xfer_len);
458
fprintf(stderr, "Not enough user memory\n");
459
res = SG_LIB_CAT_OTHER;
463
res = read(infd, wrkBuff, opts.xfer_len);
465
fprintf(stderr, "Could not read from %s", opts.ifilename);
467
} else if (res < opts.xfer_len) {
468
fprintf(stderr, "Read only %d bytes (expected %d) from %s\n",
469
res, opts.xfer_len, opts.ifilename);
472
res = sg_compare_and_write(outfd, wrkBuff, opts.numblocks, opts.lba,
473
opts.xfer_len, opts.flags, opts.verbose);
478
case SG_LIB_CAT_MEDIUM_HARD:
479
fprintf(stderr, ME "SCSI COMPARE AND WRITE "
480
"medium/hardware error\n");
482
case SG_LIB_CAT_NOT_READY:
483
fprintf(stderr, ME "device not compare_and_writey\n");
485
case SG_LIB_CAT_UNIT_ATTENTION:
486
fprintf(stderr, ME "SCSI COMPARE AND WRITE unit "
489
case SG_LIB_CAT_ABORTED_COMMAND:
490
fprintf(stderr, ME "SCSI READ aborted command\n");
493
res = SG_LIB_CAT_OTHER;
494
fprintf(stderr, ME "SCSI COMPARE AND WRITE failed\n");