1 /*
2 * Copyright (c) 2014-2015 Douglas Gilbert
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * This program issues the SCSI command WRITE AND VERIFY to a given SCSI
8 * device. It sends the command with the logical block address passed as the
9 * LBA argument, for the given number of blocks. The number of bytes sent is
10 * supplied separately, either by the size of the given file (IF) or
11 * explicitly with ILEN.
12 *
13 * This code was contributed by Bruno Goncalves
14 */
15
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #define __STDC_FORMAT_MACROS 1
27 #include <inttypes.h>
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 #include "sg_lib.h"
33 #include "sg_pt.h"
34 #include "sg_cmds_basic.h"
35 #include "sg_unaligned.h"
36 #include "sg_pr2serr.h"
37
38 static const char * version_str = "1.08 20151220";
39
40
41 #define ME "sg_write_verify: "
42
43 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
44
45 #define WRITE_VERIFY10_CMD 0x2e
46 #define WRITE_VERIFY10_CMDLEN 10
47 #define WRITE_VERIFY16_CMD 0x8e
48 #define WRITE_VERIFY16_CMDLEN 16
49
50 #define WRPROTECT_MASK (0x7)
51 #define WRPROTECT_SHIFT (5)
52
53 #define DEF_TIMEOUT_SECS 60
54
55
56 static struct option long_options[] = {
57 {"16", no_argument, 0, 'S'},
58 {"bytchk", required_argument, 0, 'b'},
59 {"dpo", no_argument, 0, 'd'},
60 {"group", required_argument, 0, 'g'},
61 {"help", no_argument, 0, 'h'},
62 {"ilen", required_argument, 0, 'I'},
63 {"in", required_argument, 0, 'i'},
64 {"lba", required_argument, 0, 'l'},
65 {"num", required_argument, 0, 'n'},
66 {"repeat", no_argument, 0, 'R'},
67 {"timeout", required_argument, 0, 't'},
68 {"verbose", no_argument, 0, 'v'},
69 {"version", no_argument, 0, 'V'},
70 {"wrprotect", required_argument, 0, 'w'},
71 {0, 0, 0, 0},
72 };
73
74
75 static void
usage()76 usage()
77 {
78 pr2serr("Usage: sg_write_verify [--16] [--bytchk=BC] [--dpo] [--group=GN] "
79 "[--help]\n"
80 " [--ilen=IL] [--in=IF] --lba=LBA "
81 "[--num=NUM]\n"
82 " [--repeat] [--timeout=TO] [--verbose] "
83 "[--version]\n"
84 " [--wrprotect=WPR] DEVICE\n"
85 " where:\n"
86 " --16|-S do WRITE AND VERIFY(16) (default: 10)\n"
87 " --bytchk=BC|-b BC set BYTCHK field (default: 0)\n"
88 " --dpo|-d set DPO bit (default: 0)\n"
89 " --group=GN|-g GN GN is group number (default: 0)\n"
90 " --help|-h print out usage message\n"
91 " --ilen=IL| -I IL input (file) length in bytes, becomes "
92 "data-out\n"
93 " buffer length (def: deduced from IF "
94 "size)\n"
95 " --in=IF|-i IF IF is a file containing the data to "
96 "be written\n"
97 " --lba=LBA|-l LBA LBA of the first block to write "
98 "and verify;\n"
99 " no default, must be given\n"
100 " --num=NUM|-n NUM logical blocks to write and verify "
101 "(def: 1)\n"
102 " --repeat|-R while IF still has data to read, send "
103 "another\n"
104 " command, bumping LBA with up to NUM "
105 "blocks again\n"
106 " --timeout=TO|-t TO command timeout in seconds (def: 60)\n"
107 " --verbose|-v increase verbosity\n"
108 " --version|-V print version string then exit\n"
109 " --wrprotect|-w WPR WPR is the WRPROTECT field value "
110 "(def: 0)\n\n"
111 "Performs a SCSI WRITE AND VERIFY (10 or 16) command on DEVICE, "
112 "startings\nat LBA for NUM logical blocks. More commands "
113 "performed only if '--repeat'\noption given. Data to be written "
114 "is fetched from the IF file.\n"
115 );
116 }
117
118 /* Invokes a SCSI WRITE AND VERIFY according with CDB. Returns 0 -> success,
119 * various SG_LIB_CAT_* positive values or -1 -> other errors */
120 static int
run_scsi_transaction(int sg_fd,const unsigned char * cdbp,int cdb_len,unsigned char * dop,int do_len,int timeout,int verbose)121 run_scsi_transaction(int sg_fd, const unsigned char *cdbp, int cdb_len,
122 unsigned char *dop, int do_len, int timeout, int verbose)
123 {
124 int res, k, sense_cat, ret;
125 unsigned char sense_b[SENSE_BUFF_LEN];
126 int noisy = 1;
127 struct sg_pt_base * ptvp;
128 char b[32];
129
130 snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len);
131 if (verbose) {
132 pr2serr(" %s cmd: ", b);
133 for (k = 0; k < cdb_len; ++k)
134 pr2serr("%02x ", cdbp[k]);
135 pr2serr("\n");
136 if ((verbose > 2) && dop && do_len) {
137 pr2serr(" Data out buffer [%d bytes]:\n", do_len);
138 dStrHexErr((const char *)dop, do_len, -1);
139 }
140 }
141 ptvp = construct_scsi_pt_obj();
142 if (NULL == ptvp) {
143 pr2serr("%s: out of memory\n", b);
144 return -1;
145 }
146 set_scsi_pt_cdb(ptvp, cdbp, cdb_len);
147 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
148 set_scsi_pt_data_out(ptvp, dop, do_len);
149 res = do_scsi_pt(ptvp, sg_fd, timeout, verbose);
150 ret = sg_cmds_process_resp(ptvp, b, res, 0, sense_b, noisy, verbose,
151 &sense_cat);
152 if (-1 == ret)
153 ;
154 else if (-2 == ret) {
155 switch (sense_cat) {
156 case SG_LIB_CAT_RECOVERED:
157 case SG_LIB_CAT_NO_SENSE:
158 ret = 0;
159 break;
160 case SG_LIB_CAT_MEDIUM_HARD: /* write or verify failed */
161 {
162 int valid, slen;
163 uint64_t ull = 0;
164
165 slen = get_scsi_pt_sense_len(ptvp);
166 valid = sg_get_sense_info_fld(sense_b, slen, &ull);
167 if (valid)
168 pr2serr("Medium or hardware error starting at lba=%"
169 PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
170 }
171 ret = sense_cat;
172 break;
173 case SG_LIB_CAT_PROTECTION: /* PI failure */
174 case SG_LIB_CAT_MISCOMPARE: /* only in bytchk=1 case */
175 default:
176 ret = sense_cat;
177 break;
178 }
179 } else
180 ret = 0;
181
182 destruct_scsi_pt_obj(ptvp);
183 return ret;
184 }
185
186 /* Invokes a SCSI WRITE AND VERIFY (10) command (SBC). Returns 0 -> success,
187 * various SG_LIB_CAT_* positive values or -1 -> other errors */
188 static int
sg_ll_write_verify10(int sg_fd,int wrprotect,int dpo,int bytchk,unsigned int lba,int num_lb,int group,unsigned char * dop,int do_len,int timeout,int verbose)189 sg_ll_write_verify10(int sg_fd, int wrprotect, int dpo, int bytchk,
190 unsigned int lba, int num_lb, int group,
191 unsigned char *dop, int do_len, int timeout, int verbose)
192 {
193 int ret;
194 unsigned char wv_cdb[WRITE_VERIFY10_CMDLEN];
195
196 memset(wv_cdb, 0, WRITE_VERIFY10_CMDLEN);
197 wv_cdb[0] = WRITE_VERIFY10_CMD;
198 wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
199 if (dpo)
200 wv_cdb[1] |= 0x10;
201 if (bytchk)
202 wv_cdb[1] |= ((bytchk & 0x3) << 1);
203
204 sg_put_unaligned_be32((uint32_t)lba, wv_cdb + 2);
205 wv_cdb[6] = group & 0x1f;
206 sg_put_unaligned_be16((uint16_t)num_lb, wv_cdb + 7);
207 ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len,
208 timeout, verbose);
209 return ret;
210 }
211
212 /* Invokes a SCSI WRITE AND VERIFY (16) command (SBC). Returns 0 -> success,
213 * various SG_LIB_CAT_* positive values or -1 -> other errors */
214 static int
sg_ll_write_verify16(int sg_fd,int wrprotect,int dpo,int bytchk,uint64_t llba,int num_lb,int group,unsigned char * dop,int do_len,int timeout,int verbose)215 sg_ll_write_verify16(int sg_fd, int wrprotect, int dpo, int bytchk,
216 uint64_t llba, int num_lb, int group, unsigned char *dop,
217 int do_len, int timeout, int verbose)
218 {
219 int ret;
220 unsigned char wv_cdb[WRITE_VERIFY16_CMDLEN];
221
222
223 memset(wv_cdb, 0, sizeof(wv_cdb));
224 wv_cdb[0] = WRITE_VERIFY16_CMD;
225 wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
226 if (dpo)
227 wv_cdb[1] |= 0x10;
228 if (bytchk)
229 wv_cdb[1] |= ((bytchk & 0x3) << 1);
230
231 sg_put_unaligned_be64(llba, wv_cdb + 2);
232 sg_put_unaligned_be32((uint32_t)num_lb, wv_cdb + 10);
233 wv_cdb[14] = group & 0x1f;
234 ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len,
235 timeout, verbose);
236 return ret;
237 }
238
239 static int
open_if(const char * fn,int got_stdin)240 open_if(const char * fn, int got_stdin)
241 {
242 int fd;
243
244 if (got_stdin)
245 fd = STDIN_FILENO;
246 else {
247 fd = open(fn, O_RDONLY);
248 if (fd < 0) {
249 pr2serr(ME "open error: %s: %s\n", fn, safe_strerror(errno));
250 return -SG_LIB_FILE_ERROR;
251 }
252 }
253 if (sg_set_binary_mode(fd) < 0) {
254 perror("sg_set_binary_mode");
255 return -SG_LIB_FILE_ERROR;
256 }
257 return fd;
258 }
259
260 int
main(int argc,char * argv[])261 main(int argc, char * argv[])
262 {
263 int sg_fd, res, c, n, first_time;
264 unsigned char * wvb = NULL;
265 void * wrkBuff = NULL;
266 int dpo = 0;
267 int bytchk = 0;
268 int group = 0;
269 int do_16 = 0;
270 int given_do_16 = 0;
271 uint64_t llba = 0;
272 int lba_given = 0;
273 uint32_t num_lb = 1;
274 uint32_t snum_lb = 1;
275 int repeat = 0;
276 int timeout = DEF_TIMEOUT_SECS;
277 int verbose = 0;
278 int64_t ll;
279 int wrprotect = 0;
280 const char * device_name = NULL;
281 const char * ifnp;
282 int has_filename = 0;
283 int ilen = -1;
284 int ifd = -1;
285 int ret = 1;
286 int b_p_lb = 512;
287 int tnum_lb_wr = 0;
288 char cmd_name[32];
289
290 ifnp = ""; /* keep MinGW quiet */
291 while (1) {
292 int option_index = 0;
293
294 c = getopt_long(argc, argv, "b:dg:hi:I:l:n:RSt:w:vV", long_options,
295 &option_index);
296 if (c == -1)
297 break;
298
299 switch (c) {
300 case 'b':
301 /* Only bytchk=0 and =1 are meaningful for this command in
302 * sbc4r02 (not =2 nor =3) but that may change in the future. */
303 bytchk = sg_get_num(optarg);
304 if ((bytchk < 0) || (bytchk > 3)) {
305 pr2serr("argument to '--bytchk' expected to be 0 to 3\n");
306 return SG_LIB_SYNTAX_ERROR;
307 }
308 break;
309 case 'd':
310 dpo = 1;
311 break;
312 case 'g':
313 group = sg_get_num(optarg);
314 if ((group < 0) || (group > 31)) {
315 pr2serr("argument to '--group' expected to be 0 to 31\n");
316 return SG_LIB_SYNTAX_ERROR;
317 }
318 break;
319 case 'h':
320 case '?':
321 usage();
322 return 0;
323 case 'i':
324 ifnp = optarg;
325 has_filename = 1;
326 break;
327 case 'I':
328 ilen = sg_get_num(optarg);
329 if (-1 == ilen) {
330 pr2serr("bad argument to '--ilen'\n");
331 return SG_LIB_SYNTAX_ERROR;
332 }
333 break;
334 case 'l':
335 if (lba_given) {
336 pr2serr("must have one and only one '--lba'\n");
337 return SG_LIB_SYNTAX_ERROR;
338 }
339 ll = sg_get_llnum(optarg);
340 if (ll < 0) {
341 pr2serr("bad argument to '--lba'\n");
342 return SG_LIB_SYNTAX_ERROR;
343 }
344 llba = (uint64_t)ll;
345 ++lba_given;
346 break;
347 case 'n':
348 n = sg_get_num(optarg);
349 if (-1 == n) {
350 pr2serr("bad argument to '--num'\n");
351 return SG_LIB_SYNTAX_ERROR;
352 }
353 num_lb = (uint32_t)n;
354 break;
355 case 'R':
356 ++repeat;
357 break;
358 case 'S':
359 do_16 = 1;
360 given_do_16 = 1;
361 break;
362 case 't':
363 timeout = sg_get_num(optarg);
364 if (timeout < 1) {
365 pr2serr("bad argument to '--timeout'\n");
366 return SG_LIB_SYNTAX_ERROR;
367 }
368 break;
369 case 'v':
370 ++verbose;
371 break;
372 case 'V':
373 pr2serr(ME "version: %s\n", version_str);
374 return 0;
375 case 'w':
376 wrprotect = sg_get_num(optarg);
377 if ((wrprotect < 0) || (wrprotect > 7)) {
378 pr2serr("wrprotect (%d) is out of range ( < %d)\n", wrprotect,
379 7);
380 return SG_LIB_SYNTAX_ERROR;
381 }
382
383 break;
384 default:
385 pr2serr("unrecognised option code 0x%x ??\n", c);
386 usage();
387 return SG_LIB_SYNTAX_ERROR;
388 }
389 }
390 if (optind < argc) {
391 if (NULL == device_name) {
392 device_name = argv[optind];
393 ++optind;
394 }
395 if (optind < argc) {
396 for (; optind < argc; ++optind)
397 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
398 usage();
399 return SG_LIB_SYNTAX_ERROR;
400 }
401 }
402
403 if (NULL == device_name) {
404 pr2serr("missing device name!\n");
405 usage();
406 return SG_LIB_SYNTAX_ERROR;
407 }
408 if (! lba_given) {
409 pr2serr("need a --lba=LBA option\n");
410 usage();
411 return SG_LIB_SYNTAX_ERROR;
412 }
413 if (repeat) {
414 if (! has_filename) {
415 pr2serr("with '--repeat' need '--in=IF' option\n");
416 usage();
417 return SG_LIB_SYNTAX_ERROR;
418 }
419 if (ilen < 1) {
420 pr2serr("with '--repeat' need '--ilen=ILEN' option\n");
421 usage();
422 return SG_LIB_SYNTAX_ERROR;
423 } else {
424 b_p_lb = ilen / num_lb;
425 if (b_p_lb < 64) {
426 pr2serr("calculated %d bytes per logical block, too small\n",
427 b_p_lb);
428 usage();
429 return SG_LIB_SYNTAX_ERROR;
430 }
431 }
432 }
433
434 sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
435 if (sg_fd < 0) {
436 pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
437 return SG_LIB_FILE_ERROR;
438 }
439
440 if ((0 == do_16) && (llba > UINT_MAX))
441 do_16 = 1;
442 if ((0 == do_16) && (num_lb > 0xffff))
443 do_16 = 1;
444 snprintf(cmd_name, sizeof(cmd_name), "Write and verify(%d)",
445 (do_16 ? 16 : 10));
446 if (verbose && (0 == given_do_16) && do_16)
447 pr2serr("Switching to %s because LBA or NUM too large\n", cmd_name);
448 if (verbose) {
449 pr2serr("Issue %s to device %s\n\tilen=%d", cmd_name, device_name,
450 ilen);
451 if (ilen > 0)
452 pr2serr(" [0x%x]", ilen);
453 pr2serr(", lba=%" PRIu64 " [0x%" PRIx64 "]\n\twrprotect=%d, dpo=%d, "
454 "bytchk=%d, group=%d, repeat=%d\n", llba, llba, wrprotect,
455 dpo, bytchk, group, repeat);
456 }
457
458 first_time = 1;
459 do {
460 if (first_time) {
461 //If a file with data to write has been provided
462 if (has_filename) {
463 struct stat a_stat;
464
465 if ((1 == strlen(ifnp)) && ('-' == ifnp[0])) {
466 ifd = STDIN_FILENO;
467 ifnp = "<stdin>";
468 if (verbose > 1)
469 pr2serr("Reading input data from stdin\n");
470 } else {
471 ifd = open_if(ifnp, 0);
472 if (ifd < 0) {
473 ret = -ifd;
474 goto err_out;
475 }
476 }
477 if (ilen < 1) {
478 if (fstat(ifd, &a_stat) < 0) {
479 pr2serr("Could not fstat(%s)\n", ifnp);
480 goto err_out;
481 }
482 if (! S_ISREG(a_stat.st_mode)) {
483 pr2serr("Cannot determine IF size, please give "
484 "'--ilen='\n");
485 goto err_out;
486 }
487 ilen = (int)a_stat.st_size;
488 if (ilen < 1) {
489 pr2serr("%s file size too small\n", ifnp);
490 goto err_out;
491 } else if (verbose)
492 pr2serr("Using file size of %d bytes\n", ilen);
493 }
494 if (NULL == (wrkBuff = malloc(ilen))) {
495 pr2serr(ME "out of memory\n");
496 ret = SG_LIB_CAT_OTHER;
497 goto err_out;
498 }
499 wvb = (unsigned char *)wrkBuff;
500 res = read(ifd, wvb, ilen);
501 if (res < 0) {
502 pr2serr("Could not read from %s", ifnp);
503 goto err_out;
504 }
505 if (res < ilen) {
506 pr2serr("Read only %d bytes (expected %d) from %s\n", res,
507 ilen, ifnp);
508 if (repeat)
509 pr2serr("Will scale subsequent pieces when repeat=1, "
510 "but this is first\n");
511 goto err_out;
512 }
513 } else {
514 if (ilen < 1) {
515 if (verbose)
516 pr2serr("Default write length to %d*%d=%d bytes\n",
517 num_lb, 512, 512 * num_lb);
518 ilen = 512 * num_lb;
519 }
520 if (NULL == (wrkBuff = malloc(ilen))) {
521 pr2serr(ME "out of memory\n");
522 ret = SG_LIB_CAT_OTHER;
523 goto err_out;
524 }
525 wvb = (unsigned char *)wrkBuff;
526 /* Not sure about this: default contents to 0xff bytes */
527 memset(wrkBuff, 0xff, ilen);
528 }
529 first_time = 0;
530 snum_lb = num_lb;
531 } else { /* repeat=1, first_time=0, must be reading file */
532 llba += snum_lb;
533 res = read(ifd, wvb, ilen);
534 if (res < 0) {
535 pr2serr("Could not read from %s", ifnp);
536 goto err_out;
537 } else {
538 if (verbose > 1)
539 pr2serr("Subsequent read from %s got %d bytes\n", ifnp, res);
540 if (0 == res)
541 break;
542 if (res < ilen) {
543 snum_lb = (uint32_t)(res / b_p_lb);
544 n = res % b_p_lb;
545 if (0 != n)
546 pr2serr(">>> warning: ignoring last %d bytes of %s\n",
547 n, ifnp);
548 if (snum_lb < 1)
549 break;
550 }
551 }
552 }
553 if (do_16)
554 res = sg_ll_write_verify16(sg_fd, wrprotect, dpo, bytchk, llba,
555 snum_lb, group, wvb, ilen, timeout,
556 verbose);
557 else
558 res = sg_ll_write_verify10(sg_fd, wrprotect, dpo, bytchk,
559 (unsigned int)llba, snum_lb, group,
560 wvb, ilen, timeout, verbose);
561 ret = res;
562 if (repeat && (0 == ret))
563 tnum_lb_wr += snum_lb;
564 if (ret || (snum_lb != num_lb))
565 break;
566 } while (repeat);
567
568 err_out:
569 if (repeat)
570 pr2serr("%d [0x%x] logical blocks written, in total\n", tnum_lb_wr,
571 tnum_lb_wr);
572 if (wrkBuff)
573 free(wrkBuff);
574 if ((ifd >= 0) && (STDIN_FILENO != ifd))
575 close(ifd);
576 res = sg_cmds_close_device(sg_fd);
577 if (res < 0) {
578 pr2serr("close error: %s\n", safe_strerror(-res));
579 if (0 == ret)
580 return SG_LIB_FILE_ERROR;
581 }
582 if (ret && (0 == verbose)) {
583 if (SG_LIB_CAT_INVALID_OP == ret)
584 pr2serr("%s command not supported\n", cmd_name);
585 else if (ret > 0)
586 pr2serr("%s, exit status %d\n", cmd_name, ret);
587 else if (ret < 0)
588 pr2serr("Some error occurred\n");
589 }
590 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
591 }
592