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