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