1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche * Copyright (c) 2009-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
10*44704f69SBart Van Assche #include <unistd.h>
11*44704f69SBart Van Assche #include <fcntl.h>
12*44704f69SBart Van Assche #include <stdio.h>
13*44704f69SBart Van Assche #include <stdlib.h>
14*44704f69SBart Van Assche #include <stdarg.h>
15*44704f69SBart Van Assche #include <stdbool.h>
16*44704f69SBart Van Assche #include <string.h>
17*44704f69SBart Van Assche #include <errno.h>
18*44704f69SBart Van Assche #include <limits.h>
19*44704f69SBart Van Assche #include <sys/types.h>
20*44704f69SBart Van Assche #include <sys/stat.h>
21*44704f69SBart Van Assche #include <getopt.h>
22*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
23*44704f69SBart Van Assche #include <inttypes.h>
24*44704f69SBart Van Assche
25*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
26*44704f69SBart Van Assche #include "config.h"
27*44704f69SBart Van Assche #endif
28*44704f69SBart Van Assche
29*44704f69SBart Van Assche #include "sg_lib.h"
30*44704f69SBart Van Assche #include "sg_pt.h"
31*44704f69SBart Van Assche #include "sg_cmds_basic.h"
32*44704f69SBart Van Assche #include "sg_cmds_extra.h"
33*44704f69SBart Van Assche #include "sg_unaligned.h"
34*44704f69SBart Van Assche #include "sg_pr2serr.h"
35*44704f69SBart Van Assche
36*44704f69SBart Van Assche static const char * version_str = "1.34 20220127";
37*44704f69SBart Van Assche
38*44704f69SBart Van Assche
39*44704f69SBart Van Assche #define ME "sg_write_same: "
40*44704f69SBart Van Assche
41*44704f69SBart Van Assche #define WRITE_SAME10_OP 0x41
42*44704f69SBart Van Assche #define WRITE_SAME16_OP 0x93
43*44704f69SBart Van Assche #define VARIABLE_LEN_OP 0x7f
44*44704f69SBart Van Assche #define WRITE_SAME32_SA 0xd
45*44704f69SBart Van Assche #define WRITE_SAME32_ADD 0x18
46*44704f69SBart Van Assche #define WRITE_SAME10_LEN 10
47*44704f69SBart Van Assche #define WRITE_SAME16_LEN 16
48*44704f69SBart Van Assche #define WRITE_SAME32_LEN 32
49*44704f69SBart Van Assche #define RCAP10_RESP_LEN 8
50*44704f69SBart Van Assche #define RCAP16_RESP_LEN 32
51*44704f69SBart Van Assche #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
52*44704f69SBart Van Assche #define DEF_TIMEOUT_SECS 60
53*44704f69SBart Van Assche #define DEF_WS_CDB_SIZE WRITE_SAME10_LEN
54*44704f69SBart Van Assche #define DEF_WS_NUMBLOCKS 1
55*44704f69SBart Van Assche #define MAX_XFER_LEN (64 * 1024)
56*44704f69SBart Van Assche #define EBUFF_SZ 512
57*44704f69SBart Van Assche
58*44704f69SBart Van Assche #ifndef UINT32_MAX
59*44704f69SBart Van Assche #define UINT32_MAX ((uint32_t)-1)
60*44704f69SBart Van Assche #endif
61*44704f69SBart Van Assche
62*44704f69SBart Van Assche static struct option long_options[] = {
63*44704f69SBart Van Assche {"10", no_argument, 0, 'R'},
64*44704f69SBart Van Assche {"16", no_argument, 0, 'S'},
65*44704f69SBart Van Assche {"32", no_argument, 0, 'T'},
66*44704f69SBart Van Assche {"anchor", no_argument, 0, 'a'},
67*44704f69SBart Van Assche {"ff", no_argument, 0, 'f'},
68*44704f69SBart Van Assche {"grpnum", required_argument, 0, 'g'},
69*44704f69SBart Van Assche {"help", no_argument, 0, 'h'},
70*44704f69SBart Van Assche {"in", required_argument, 0, 'i'},
71*44704f69SBart Van Assche {"lba", required_argument, 0, 'l'},
72*44704f69SBart Van Assche {"lbdata", no_argument, 0, 'L'},
73*44704f69SBart Van Assche {"ndob", no_argument, 0, 'N'},
74*44704f69SBart Van Assche {"num", required_argument, 0, 'n'},
75*44704f69SBart Van Assche {"pbdata", no_argument, 0, 'P'},
76*44704f69SBart Van Assche {"timeout", required_argument, 0, 't'},
77*44704f69SBart Van Assche {"unmap", no_argument, 0, 'U'},
78*44704f69SBart Van Assche {"verbose", no_argument, 0, 'v'},
79*44704f69SBart Van Assche {"version", no_argument, 0, 'V'},
80*44704f69SBart Van Assche {"wrprotect", required_argument, 0, 'w'},
81*44704f69SBart Van Assche {"xferlen", required_argument, 0, 'x'},
82*44704f69SBart Van Assche {0, 0, 0, 0},
83*44704f69SBart Van Assche };
84*44704f69SBart Van Assche
85*44704f69SBart Van Assche struct opts_t {
86*44704f69SBart Van Assche bool anchor;
87*44704f69SBart Van Assche bool ff;
88*44704f69SBart Van Assche bool ndob;
89*44704f69SBart Van Assche bool lbdata;
90*44704f69SBart Van Assche bool pbdata;
91*44704f69SBart Van Assche bool unmap;
92*44704f69SBart Van Assche bool verbose_given;
93*44704f69SBart Van Assche bool version_given;
94*44704f69SBart Van Assche bool want_ws10;
95*44704f69SBart Van Assche int grpnum;
96*44704f69SBart Van Assche int numblocks;
97*44704f69SBart Van Assche int timeout;
98*44704f69SBart Van Assche int verbose;
99*44704f69SBart Van Assche int wrprotect;
100*44704f69SBart Van Assche int xfer_len;
101*44704f69SBart Van Assche int pref_cdb_size;
102*44704f69SBart Van Assche uint64_t lba;
103*44704f69SBart Van Assche char ifilename[256];
104*44704f69SBart Van Assche };
105*44704f69SBart Van Assche
106*44704f69SBart Van Assche
107*44704f69SBart Van Assche static void
usage()108*44704f69SBart Van Assche usage()
109*44704f69SBart Van Assche {
110*44704f69SBart Van Assche pr2serr("Usage: sg_write_same [--10] [--16] [--32] [--anchor] "
111*44704f69SBart Van Assche "[-ff] [--grpnum=GN]\n"
112*44704f69SBart Van Assche " [--help] [--in=IF] [--lba=LBA] [--lbdata] "
113*44704f69SBart Van Assche "[--ndob]\n"
114*44704f69SBart Van Assche " [--num=NUM] [--pbdata] [--timeout=TO] "
115*44704f69SBart Van Assche "[--unmap]\n"
116*44704f69SBart Van Assche " [--verbose] [--version] [--wrprotect=WRP] "
117*44704f69SBart Van Assche "[xferlen=LEN]\n"
118*44704f69SBart Van Assche " DEVICE\n"
119*44704f69SBart Van Assche " where:\n"
120*44704f69SBart Van Assche " --10|-R send WRITE SAME(10) (even if '--unmap' "
121*44704f69SBart Van Assche "is given)\n"
122*44704f69SBart Van Assche " --16|-S send WRITE SAME(16) (def: 10 unless "
123*44704f69SBart Van Assche "'--unmap' given,\n"
124*44704f69SBart Van Assche " LBA+NUM > 32 bits, or NUM > 65535; "
125*44704f69SBart Van Assche "then def 16)\n"
126*44704f69SBart Van Assche " --32|-T send WRITE SAME(32) (def: 10 or 16)\n"
127*44704f69SBart Van Assche " --anchor|-a set ANCHOR field in cdb\n"
128*44704f69SBart Van Assche " --ff|-f use buffer of 0xff bytes for fill "
129*44704f69SBart Van Assche "(def: 0x0 bytes)\n"
130*44704f69SBart Van Assche " --grpnum=GN|-g GN GN is group number field (def: 0)\n"
131*44704f69SBart Van Assche " --help|-h print out usage message\n"
132*44704f69SBart Van Assche " --in=IF|-i IF IF is file to fetch one block of data "
133*44704f69SBart Van Assche "from (use LEN\n"
134*44704f69SBart Van Assche " bytes or whole file). Block written to "
135*44704f69SBart Van Assche "DEVICE\n"
136*44704f69SBart Van Assche " --lba=LBA|-l LBA LBA is the logical block address to "
137*44704f69SBart Van Assche "start (def: 0)\n"
138*44704f69SBart Van Assche " --lbdata|-L set LBDATA bit (obsolete)\n"
139*44704f69SBart Van Assche " --ndob|-N set NDOB (no data-out buffer) bit in "
140*44704f69SBart Van Assche "cdb\n"
141*44704f69SBart Van Assche " --num=NUM|-n NUM NUM is number of logical blocks to "
142*44704f69SBart Van Assche "write (def: 1)\n"
143*44704f69SBart Van Assche " [Beware NUM==0 may mean: 'rest of "
144*44704f69SBart Van Assche "device']\n"
145*44704f69SBart Van Assche " --pbdata|-P set PBDATA bit (obsolete)\n"
146*44704f69SBart Van Assche " --timeout=TO|-t TO command timeout (unit: seconds) (def: "
147*44704f69SBart Van Assche "60)\n"
148*44704f69SBart Van Assche " --unmap|-U set UNMAP bit\n"
149*44704f69SBart Van Assche " --verbose|-v increase verbosity\n"
150*44704f69SBart Van Assche " --version|-V print version string then exit\n"
151*44704f69SBart Van Assche " --wrprotect=WPR|-w WPR WPR is the WRPROTECT field value "
152*44704f69SBart Van Assche "(def: 0)\n"
153*44704f69SBart Van Assche " --xferlen=LEN|-x LEN LEN is number of bytes from IF to "
154*44704f69SBart Van Assche "send to\n"
155*44704f69SBart Van Assche " DEVICE (def: IF file length)\n\n"
156*44704f69SBart Van Assche "Performs a SCSI WRITE SAME (10, 16 or 32) command. NDOB bit is "
157*44704f69SBart Van Assche "only\nsupported by the 16 and 32 byte variants. When set the "
158*44704f69SBart Van Assche "specified blocks\nwill be filled with zeros or the "
159*44704f69SBart Van Assche "'provisioning initialization pattern'\nas indicated by the "
160*44704f69SBart Van Assche "LBPRZ field. As a precaution one of the '--in=',\n'--lba=' or "
161*44704f69SBart Van Assche "'--num=' options is required.\nAnother implementation of WRITE "
162*44704f69SBart Van Assche "SAME is found in the sg_write_x utility.\n"
163*44704f69SBart Van Assche );
164*44704f69SBart Van Assche }
165*44704f69SBart Van Assche
166*44704f69SBart Van Assche static int
do_write_same(int sg_fd,const struct opts_t * op,const void * dataoutp,int * act_cdb_lenp)167*44704f69SBart Van Assche do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp,
168*44704f69SBart Van Assche int * act_cdb_lenp)
169*44704f69SBart Van Assche {
170*44704f69SBart Van Assche int ret, res, sense_cat, cdb_len;
171*44704f69SBart Van Assche uint64_t llba;
172*44704f69SBart Van Assche uint8_t ws_cdb[WRITE_SAME32_LEN] SG_C_CPP_ZERO_INIT;
173*44704f69SBart Van Assche uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
174*44704f69SBart Van Assche struct sg_pt_base * ptvp;
175*44704f69SBart Van Assche
176*44704f69SBart Van Assche cdb_len = op->pref_cdb_size;
177*44704f69SBart Van Assche if (WRITE_SAME10_LEN == cdb_len) {
178*44704f69SBart Van Assche llba = op->lba + op->numblocks;
179*44704f69SBart Van Assche if ((op->numblocks > 0xffff) || (llba > UINT32_MAX) ||
180*44704f69SBart Van Assche op->ndob || (op->unmap && (! op->want_ws10))) {
181*44704f69SBart Van Assche cdb_len = WRITE_SAME16_LEN;
182*44704f69SBart Van Assche if (op->verbose) {
183*44704f69SBart Van Assche const char * cp = "use WRITE SAME(16) instead of 10 byte "
184*44704f69SBart Van Assche "cdb";
185*44704f69SBart Van Assche
186*44704f69SBart Van Assche if (op->numblocks > 0xffff)
187*44704f69SBart Van Assche pr2serr("%s since blocks exceed 65535\n", cp);
188*44704f69SBart Van Assche else if (llba > UINT32_MAX)
189*44704f69SBart Van Assche pr2serr("%s since LBA may exceed 32 bits\n", cp);
190*44704f69SBart Van Assche else
191*44704f69SBart Van Assche pr2serr("%s due to ndob or unmap settings\n", cp);
192*44704f69SBart Van Assche }
193*44704f69SBart Van Assche }
194*44704f69SBart Van Assche }
195*44704f69SBart Van Assche if (act_cdb_lenp)
196*44704f69SBart Van Assche *act_cdb_lenp = cdb_len;
197*44704f69SBart Van Assche switch (cdb_len) {
198*44704f69SBart Van Assche case WRITE_SAME10_LEN:
199*44704f69SBart Van Assche ws_cdb[0] = WRITE_SAME10_OP;
200*44704f69SBart Van Assche ws_cdb[1] = ((op->wrprotect & 0x7) << 5);
201*44704f69SBart Van Assche /* ANCHOR + UNMAP not allowed for WRITE_SAME10 in sbc3r24+r25 but
202*44704f69SBart Van Assche * a proposal has been made to allow it. Anticipate approval. */
203*44704f69SBart Van Assche if (op->anchor)
204*44704f69SBart Van Assche ws_cdb[1] |= 0x10;
205*44704f69SBart Van Assche if (op->unmap)
206*44704f69SBart Van Assche ws_cdb[1] |= 0x8;
207*44704f69SBart Van Assche if (op->pbdata)
208*44704f69SBart Van Assche ws_cdb[1] |= 0x4;
209*44704f69SBart Van Assche if (op->lbdata)
210*44704f69SBart Van Assche ws_cdb[1] |= 0x2;
211*44704f69SBart Van Assche sg_put_unaligned_be32((uint32_t)op->lba, ws_cdb + 2);
212*44704f69SBart Van Assche ws_cdb[6] = (op->grpnum & GRPNUM_MASK);
213*44704f69SBart Van Assche sg_put_unaligned_be16((uint16_t)op->numblocks, ws_cdb + 7);
214*44704f69SBart Van Assche break;
215*44704f69SBart Van Assche case WRITE_SAME16_LEN:
216*44704f69SBart Van Assche ws_cdb[0] = WRITE_SAME16_OP;
217*44704f69SBart Van Assche ws_cdb[1] = ((op->wrprotect & 0x7) << 5);
218*44704f69SBart Van Assche if (op->anchor)
219*44704f69SBart Van Assche ws_cdb[1] |= 0x10;
220*44704f69SBart Van Assche if (op->unmap)
221*44704f69SBart Van Assche ws_cdb[1] |= 0x8;
222*44704f69SBart Van Assche if (op->pbdata)
223*44704f69SBart Van Assche ws_cdb[1] |= 0x4;
224*44704f69SBart Van Assche if (op->lbdata)
225*44704f69SBart Van Assche ws_cdb[1] |= 0x2;
226*44704f69SBart Van Assche if (op->ndob)
227*44704f69SBart Van Assche ws_cdb[1] |= 0x1;
228*44704f69SBart Van Assche sg_put_unaligned_be64(op->lba, ws_cdb + 2);
229*44704f69SBart Van Assche sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 10);
230*44704f69SBart Van Assche ws_cdb[14] = (op->grpnum & GRPNUM_MASK);
231*44704f69SBart Van Assche break;
232*44704f69SBart Van Assche case WRITE_SAME32_LEN:
233*44704f69SBart Van Assche ws_cdb[0] = VARIABLE_LEN_OP;
234*44704f69SBart Van Assche ws_cdb[6] = (op->grpnum & GRPNUM_MASK);
235*44704f69SBart Van Assche ws_cdb[7] = WRITE_SAME32_ADD;
236*44704f69SBart Van Assche sg_put_unaligned_be16((uint16_t)WRITE_SAME32_SA, ws_cdb + 8);
237*44704f69SBart Van Assche ws_cdb[10] = ((op->wrprotect & 0x7) << 5);
238*44704f69SBart Van Assche if (op->anchor)
239*44704f69SBart Van Assche ws_cdb[10] |= 0x10;
240*44704f69SBart Van Assche if (op->unmap)
241*44704f69SBart Van Assche ws_cdb[10] |= 0x8;
242*44704f69SBart Van Assche if (op->pbdata)
243*44704f69SBart Van Assche ws_cdb[10] |= 0x4;
244*44704f69SBart Van Assche if (op->lbdata)
245*44704f69SBart Van Assche ws_cdb[10] |= 0x2;
246*44704f69SBart Van Assche if (op->ndob)
247*44704f69SBart Van Assche ws_cdb[10] |= 0x1;
248*44704f69SBart Van Assche sg_put_unaligned_be64(op->lba, ws_cdb + 12);
249*44704f69SBart Van Assche sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 28);
250*44704f69SBart Van Assche break;
251*44704f69SBart Van Assche default:
252*44704f69SBart Van Assche pr2serr("do_write_same: bad cdb length %d\n", cdb_len);
253*44704f69SBart Van Assche return -1;
254*44704f69SBart Van Assche }
255*44704f69SBart Van Assche
256*44704f69SBart Van Assche if (op->verbose > 1) {
257*44704f69SBart Van Assche char b[128];
258*44704f69SBart Van Assche
259*44704f69SBart Van Assche pr2serr(" Write same(%d) cdb: %s\n", cdb_len,
260*44704f69SBart Van Assche sg_get_command_str(ws_cdb, cdb_len, false, sizeof(b), b));
261*44704f69SBart Van Assche pr2serr(" Data-out buffer length=%d\n", op->xfer_len);
262*44704f69SBart Van Assche }
263*44704f69SBart Van Assche if ((op->verbose > 3) && (op->xfer_len > 0)) {
264*44704f69SBart Van Assche pr2serr(" Data-out buffer contents:\n");
265*44704f69SBart Van Assche hex2stderr((const uint8_t *)dataoutp, op->xfer_len, 1);
266*44704f69SBart Van Assche }
267*44704f69SBart Van Assche ptvp = construct_scsi_pt_obj();
268*44704f69SBart Van Assche if (NULL == ptvp) {
269*44704f69SBart Van Assche pr2serr("Write same(%d): out of memory\n", cdb_len);
270*44704f69SBart Van Assche return -1;
271*44704f69SBart Van Assche }
272*44704f69SBart Van Assche set_scsi_pt_cdb(ptvp, ws_cdb, cdb_len);
273*44704f69SBart Van Assche set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
274*44704f69SBart Van Assche set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, op->xfer_len);
275*44704f69SBart Van Assche res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose);
276*44704f69SBart Van Assche ret = sg_cmds_process_resp(ptvp, "Write same", res, true /*noisy */,
277*44704f69SBart Van Assche op->verbose, &sense_cat);
278*44704f69SBart Van Assche if (-1 == ret) {
279*44704f69SBart Van Assche if (get_scsi_pt_transport_err(ptvp))
280*44704f69SBart Van Assche ret = SG_LIB_TRANSPORT_ERROR;
281*44704f69SBart Van Assche else
282*44704f69SBart Van Assche ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
283*44704f69SBart Van Assche } else if (-2 == ret) {
284*44704f69SBart Van Assche switch (sense_cat) {
285*44704f69SBart Van Assche case SG_LIB_CAT_RECOVERED:
286*44704f69SBart Van Assche case SG_LIB_CAT_NO_SENSE:
287*44704f69SBart Van Assche ret = 0;
288*44704f69SBart Van Assche break;
289*44704f69SBart Van Assche case SG_LIB_CAT_MEDIUM_HARD:
290*44704f69SBart Van Assche {
291*44704f69SBart Van Assche bool valid;
292*44704f69SBart Van Assche int slen;
293*44704f69SBart Van Assche uint64_t ull = 0;
294*44704f69SBart Van Assche
295*44704f69SBart Van Assche slen = get_scsi_pt_sense_len(ptvp);
296*44704f69SBart Van Assche valid = sg_get_sense_info_fld(sense_b, slen, &ull);
297*44704f69SBart Van Assche if (valid)
298*44704f69SBart Van Assche pr2serr("Medium or hardware error starting at lba=%"
299*44704f69SBart Van Assche PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
300*44704f69SBart Van Assche }
301*44704f69SBart Van Assche ret = sense_cat;
302*44704f69SBart Van Assche break;
303*44704f69SBart Van Assche case SG_LIB_CAT_ILLEGAL_REQ:
304*44704f69SBart Van Assche if (op->verbose)
305*44704f69SBart Van Assche sg_print_command_len(ws_cdb, cdb_len);
306*44704f69SBart Van Assche /* FALL THROUGH */
307*44704f69SBart Van Assche default:
308*44704f69SBart Van Assche ret = sense_cat;
309*44704f69SBart Van Assche break;
310*44704f69SBart Van Assche }
311*44704f69SBart Van Assche } else
312*44704f69SBart Van Assche ret = 0;
313*44704f69SBart Van Assche
314*44704f69SBart Van Assche destruct_scsi_pt_obj(ptvp);
315*44704f69SBart Van Assche return ret;
316*44704f69SBart Van Assche }
317*44704f69SBart Van Assche
318*44704f69SBart Van Assche
319*44704f69SBart Van Assche int
main(int argc,char * argv[])320*44704f69SBart Van Assche main(int argc, char * argv[])
321*44704f69SBart Van Assche {
322*44704f69SBart Van Assche bool got_stdin = false;
323*44704f69SBart Van Assche bool if_given = false;
324*44704f69SBart Van Assche bool lba_given = false;
325*44704f69SBart Van Assche bool num_given = false;
326*44704f69SBart Van Assche bool prot_en;
327*44704f69SBart Van Assche int res, c, infd, act_cdb_len, vb, err;
328*44704f69SBart Van Assche int sg_fd = -1;
329*44704f69SBart Van Assche int ret = -1;
330*44704f69SBart Van Assche uint32_t block_size;
331*44704f69SBart Van Assche int64_t ll;
332*44704f69SBart Van Assche const char * device_name = NULL;
333*44704f69SBart Van Assche struct opts_t * op;
334*44704f69SBart Van Assche uint8_t * wBuff = NULL;
335*44704f69SBart Van Assche uint8_t * free_wBuff = NULL;
336*44704f69SBart Van Assche char ebuff[EBUFF_SZ];
337*44704f69SBart Van Assche char b[80];
338*44704f69SBart Van Assche uint8_t resp_buff[RCAP16_RESP_LEN];
339*44704f69SBart Van Assche struct opts_t opts;
340*44704f69SBart Van Assche struct stat a_stat;
341*44704f69SBart Van Assche
342*44704f69SBart Van Assche op = &opts;
343*44704f69SBart Van Assche memset(op, 0, sizeof(opts));
344*44704f69SBart Van Assche op->numblocks = DEF_WS_NUMBLOCKS;
345*44704f69SBart Van Assche op->pref_cdb_size = DEF_WS_CDB_SIZE;
346*44704f69SBart Van Assche op->timeout = DEF_TIMEOUT_SECS;
347*44704f69SBart Van Assche while (1) {
348*44704f69SBart Van Assche int option_index = 0;
349*44704f69SBart Van Assche
350*44704f69SBart Van Assche c = getopt_long(argc, argv, "afg:hi:l:Ln:NPRSt:TUvVw:x:",
351*44704f69SBart Van Assche long_options, &option_index);
352*44704f69SBart Van Assche if (c == -1)
353*44704f69SBart Van Assche break;
354*44704f69SBart Van Assche
355*44704f69SBart Van Assche switch (c) {
356*44704f69SBart Van Assche case 'a':
357*44704f69SBart Van Assche op->anchor = true;
358*44704f69SBart Van Assche break;
359*44704f69SBart Van Assche case 'f':
360*44704f69SBart Van Assche op->ff = true;
361*44704f69SBart Van Assche break;
362*44704f69SBart Van Assche case 'g':
363*44704f69SBart Van Assche op->grpnum = sg_get_num(optarg);
364*44704f69SBart Van Assche if ((op->grpnum < 0) || (op->grpnum > 63)) {
365*44704f69SBart Van Assche pr2serr("bad argument to '--grpnum'\n");
366*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
367*44704f69SBart Van Assche }
368*44704f69SBart Van Assche break;
369*44704f69SBart Van Assche case 'h':
370*44704f69SBart Van Assche case '?':
371*44704f69SBart Van Assche usage();
372*44704f69SBart Van Assche return 0;
373*44704f69SBart Van Assche case 'i':
374*44704f69SBart Van Assche strncpy(op->ifilename, optarg, sizeof(op->ifilename) - 1);
375*44704f69SBart Van Assche op->ifilename[sizeof(op->ifilename) - 1] = '\0';
376*44704f69SBart Van Assche if_given = true;
377*44704f69SBart Van Assche break;
378*44704f69SBart Van Assche case 'l':
379*44704f69SBart Van Assche ll = sg_get_llnum(optarg);
380*44704f69SBart Van Assche if (-1 == ll) {
381*44704f69SBart Van Assche pr2serr("bad argument to '--lba'\n");
382*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
383*44704f69SBart Van Assche }
384*44704f69SBart Van Assche op->lba = (uint64_t)ll;
385*44704f69SBart Van Assche lba_given = true;
386*44704f69SBart Van Assche break;
387*44704f69SBart Van Assche case 'L':
388*44704f69SBart Van Assche op->lbdata = true;
389*44704f69SBart Van Assche break;
390*44704f69SBart Van Assche case 'n':
391*44704f69SBart Van Assche op->numblocks = sg_get_num(optarg);
392*44704f69SBart Van Assche if (op->numblocks < 0) {
393*44704f69SBart Van Assche pr2serr("bad argument to '--num'\n");
394*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
395*44704f69SBart Van Assche }
396*44704f69SBart Van Assche num_given = true;
397*44704f69SBart Van Assche break;
398*44704f69SBart Van Assche case 'N':
399*44704f69SBart Van Assche op->ndob = true;
400*44704f69SBart Van Assche break;
401*44704f69SBart Van Assche case 'P':
402*44704f69SBart Van Assche op->pbdata = true;
403*44704f69SBart Van Assche break;
404*44704f69SBart Van Assche case 'R':
405*44704f69SBart Van Assche op->want_ws10 = true;
406*44704f69SBart Van Assche break;
407*44704f69SBart Van Assche case 'S':
408*44704f69SBart Van Assche if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
409*44704f69SBart Van Assche pr2serr("only one '--10', '--16' or '--32' please\n");
410*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
411*44704f69SBart Van Assche }
412*44704f69SBart Van Assche op->pref_cdb_size = 16;
413*44704f69SBart Van Assche break;
414*44704f69SBart Van Assche case 't':
415*44704f69SBart Van Assche op->timeout = sg_get_num(optarg);
416*44704f69SBart Van Assche if (op->timeout < 0) {
417*44704f69SBart Van Assche pr2serr("bad argument to '--timeout'\n");
418*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
419*44704f69SBart Van Assche }
420*44704f69SBart Van Assche break;
421*44704f69SBart Van Assche case 'T':
422*44704f69SBart Van Assche if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
423*44704f69SBart Van Assche pr2serr("only one '--10', '--16' or '--32' please\n");
424*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
425*44704f69SBart Van Assche }
426*44704f69SBart Van Assche op->pref_cdb_size = 32;
427*44704f69SBart Van Assche break;
428*44704f69SBart Van Assche case 'U':
429*44704f69SBart Van Assche op->unmap = true;
430*44704f69SBart Van Assche break;
431*44704f69SBart Van Assche case 'v':
432*44704f69SBart Van Assche op->verbose_given = true;
433*44704f69SBart Van Assche ++op->verbose;
434*44704f69SBart Van Assche break;
435*44704f69SBart Van Assche case 'V':
436*44704f69SBart Van Assche op->version_given = true;
437*44704f69SBart Van Assche break;
438*44704f69SBart Van Assche case 'w':
439*44704f69SBart Van Assche op->wrprotect = sg_get_num(optarg);
440*44704f69SBart Van Assche if ((op->wrprotect < 0) || (op->wrprotect > 7)) {
441*44704f69SBart Van Assche pr2serr("bad argument to '--wrprotect'\n");
442*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
443*44704f69SBart Van Assche }
444*44704f69SBart Van Assche break;
445*44704f69SBart Van Assche case 'x':
446*44704f69SBart Van Assche op->xfer_len = sg_get_num(optarg);
447*44704f69SBart Van Assche if (op->xfer_len < 0) {
448*44704f69SBart Van Assche pr2serr("bad argument to '--xferlen'\n");
449*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
450*44704f69SBart Van Assche }
451*44704f69SBart Van Assche break;
452*44704f69SBart Van Assche default:
453*44704f69SBart Van Assche pr2serr("unrecognised option code 0x%x ??\n", c);
454*44704f69SBart Van Assche usage();
455*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
456*44704f69SBart Van Assche }
457*44704f69SBart Van Assche }
458*44704f69SBart Van Assche if (optind < argc) {
459*44704f69SBart Van Assche if (NULL == device_name) {
460*44704f69SBart Van Assche device_name = argv[optind];
461*44704f69SBart Van Assche ++optind;
462*44704f69SBart Van Assche }
463*44704f69SBart Van Assche if (optind < argc) {
464*44704f69SBart Van Assche for (; optind < argc; ++optind)
465*44704f69SBart Van Assche pr2serr("Unexpected extra argument: %s\n", argv[optind]);
466*44704f69SBart Van Assche usage();
467*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
468*44704f69SBart Van Assche }
469*44704f69SBart Van Assche }
470*44704f69SBart Van Assche if (op->want_ws10 && (DEF_WS_CDB_SIZE != op->pref_cdb_size)) {
471*44704f69SBart Van Assche pr2serr("only one '--10', '--16' or '--32' please\n");
472*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
473*44704f69SBart Van Assche }
474*44704f69SBart Van Assche
475*44704f69SBart Van Assche #ifdef DEBUG
476*44704f69SBart Van Assche pr2serr("In DEBUG mode, ");
477*44704f69SBart Van Assche if (op->verbose_given && op->version_given) {
478*44704f69SBart Van Assche pr2serr("but override: '-vV' given, zero verbose and continue\n");
479*44704f69SBart Van Assche op->verbose_given = false;
480*44704f69SBart Van Assche op->version_given = false;
481*44704f69SBart Van Assche op->verbose = 0;
482*44704f69SBart Van Assche } else if (! op->verbose_given) {
483*44704f69SBart Van Assche pr2serr("set '-vv'\n");
484*44704f69SBart Van Assche op->verbose = 2;
485*44704f69SBart Van Assche } else
486*44704f69SBart Van Assche pr2serr("keep verbose=%d\n", op->verbose);
487*44704f69SBart Van Assche #else
488*44704f69SBart Van Assche if (op->verbose_given && op->version_given)
489*44704f69SBart Van Assche pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
490*44704f69SBart Van Assche #endif
491*44704f69SBart Van Assche if (op->version_given) {
492*44704f69SBart Van Assche pr2serr(ME "version: %s\n", version_str);
493*44704f69SBart Van Assche return 0;
494*44704f69SBart Van Assche }
495*44704f69SBart Van Assche
496*44704f69SBart Van Assche if (NULL == device_name) {
497*44704f69SBart Van Assche pr2serr("Missing device name!\n\n");
498*44704f69SBart Van Assche usage();
499*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
500*44704f69SBart Van Assche }
501*44704f69SBart Van Assche vb = op->verbose;
502*44704f69SBart Van Assche
503*44704f69SBart Van Assche if ((! if_given) && (! lba_given) && (! num_given)) {
504*44704f69SBart Van Assche pr2serr("As a precaution, one of '--in=', '--lba=' or '--num=' is "
505*44704f69SBart Van Assche "required\n");
506*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
507*44704f69SBart Van Assche }
508*44704f69SBart Van Assche
509*44704f69SBart Van Assche if (op->ndob) {
510*44704f69SBart Van Assche if (if_given) {
511*44704f69SBart Van Assche pr2serr("Can't have both --ndob and '--in='\n");
512*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
513*44704f69SBart Van Assche }
514*44704f69SBart Van Assche if (0 != op->xfer_len) {
515*44704f69SBart Van Assche pr2serr("With --ndob only '--xferlen=0' (or not given) is "
516*44704f69SBart Van Assche "acceptable\n");
517*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
518*44704f69SBart Van Assche }
519*44704f69SBart Van Assche } else if (op->ifilename[0]) {
520*44704f69SBart Van Assche got_stdin = (0 == strcmp(op->ifilename, "-"));
521*44704f69SBart Van Assche if (! got_stdin) {
522*44704f69SBart Van Assche memset(&a_stat, 0, sizeof(a_stat));
523*44704f69SBart Van Assche if (stat(op->ifilename, &a_stat) < 0) {
524*44704f69SBart Van Assche err = errno;
525*44704f69SBart Van Assche if (vb)
526*44704f69SBart Van Assche pr2serr("unable to stat(%s): %s\n", op->ifilename,
527*44704f69SBart Van Assche safe_strerror(err));
528*44704f69SBart Van Assche return sg_convert_errno(err);
529*44704f69SBart Van Assche }
530*44704f69SBart Van Assche if (op->xfer_len <= 0)
531*44704f69SBart Van Assche op->xfer_len = (int)a_stat.st_size;
532*44704f69SBart Van Assche }
533*44704f69SBart Van Assche }
534*44704f69SBart Van Assche
535*44704f69SBart Van Assche sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
536*44704f69SBart Van Assche if (sg_fd < 0) {
537*44704f69SBart Van Assche if (op->verbose)
538*44704f69SBart Van Assche pr2serr(ME "open error: %s: %s\n", device_name,
539*44704f69SBart Van Assche safe_strerror(-sg_fd));
540*44704f69SBart Van Assche ret = sg_convert_errno(-sg_fd);
541*44704f69SBart Van Assche goto err_out;
542*44704f69SBart Van Assche }
543*44704f69SBart Van Assche
544*44704f69SBart Van Assche if (! op->ndob) {
545*44704f69SBart Van Assche prot_en = false;
546*44704f69SBart Van Assche if (0 == op->xfer_len) {
547*44704f69SBart Van Assche res = sg_ll_readcap_16(sg_fd, false /* pmi */, 0 /* llba */,
548*44704f69SBart Van Assche resp_buff, RCAP16_RESP_LEN, true,
549*44704f69SBart Van Assche (vb ? (vb - 1): 0));
550*44704f69SBart Van Assche if (SG_LIB_CAT_UNIT_ATTENTION == res) {
551*44704f69SBart Van Assche pr2serr("Read capacity(16) unit attention, try again\n");
552*44704f69SBart Van Assche res = sg_ll_readcap_16(sg_fd, false, 0, resp_buff,
553*44704f69SBart Van Assche RCAP16_RESP_LEN, true,
554*44704f69SBart Van Assche (vb ? (vb - 1): 0));
555*44704f69SBart Van Assche }
556*44704f69SBart Van Assche if (0 == res) {
557*44704f69SBart Van Assche if (vb > 3)
558*44704f69SBart Van Assche hex2stderr(resp_buff, RCAP16_RESP_LEN, 1);
559*44704f69SBart Van Assche block_size = sg_get_unaligned_be32(resp_buff + 8);
560*44704f69SBart Van Assche prot_en = !!(resp_buff[12] & 0x1);
561*44704f69SBart Van Assche op->xfer_len = block_size;
562*44704f69SBart Van Assche if (prot_en && (op->wrprotect > 0))
563*44704f69SBart Van Assche op->xfer_len += 8;
564*44704f69SBart Van Assche } else if ((SG_LIB_CAT_INVALID_OP == res) ||
565*44704f69SBart Van Assche (SG_LIB_CAT_ILLEGAL_REQ == res)) {
566*44704f69SBart Van Assche if (vb)
567*44704f69SBart Van Assche pr2serr("Read capacity(16) not supported, try Read "
568*44704f69SBart Van Assche "capacity(10)\n");
569*44704f69SBart Van Assche res = sg_ll_readcap_10(sg_fd, false /* pmi */, 0 /* lba */,
570*44704f69SBart Van Assche resp_buff, RCAP10_RESP_LEN, true,
571*44704f69SBart Van Assche (vb ? (vb - 1): 0));
572*44704f69SBart Van Assche if (0 == res) {
573*44704f69SBart Van Assche if (vb > 3)
574*44704f69SBart Van Assche hex2stderr(resp_buff, RCAP10_RESP_LEN, 1);
575*44704f69SBart Van Assche block_size = sg_get_unaligned_be32(resp_buff + 4);
576*44704f69SBart Van Assche op->xfer_len = block_size;
577*44704f69SBart Van Assche } else {
578*44704f69SBart Van Assche sg_get_category_sense_str(res, sizeof(b), b, vb);
579*44704f69SBart Van Assche pr2serr("Read capacity(10): %s\n", b);
580*44704f69SBart Van Assche pr2serr("Unable to calculate block size\n");
581*44704f69SBart Van Assche }
582*44704f69SBart Van Assche } else if (vb) {
583*44704f69SBart Van Assche sg_get_category_sense_str(res, sizeof(b), b, vb);
584*44704f69SBart Van Assche pr2serr("Read capacity(16): %s\n", b);
585*44704f69SBart Van Assche pr2serr("Unable to calculate block size\n");
586*44704f69SBart Van Assche }
587*44704f69SBart Van Assche }
588*44704f69SBart Van Assche if (op->xfer_len < 1) {
589*44704f69SBart Van Assche pr2serr("unable to deduce block size, please give '--xferlen=' "
590*44704f69SBart Van Assche "argument\n");
591*44704f69SBart Van Assche ret = SG_LIB_SYNTAX_ERROR;
592*44704f69SBart Van Assche goto err_out;
593*44704f69SBart Van Assche }
594*44704f69SBart Van Assche if (op->xfer_len > MAX_XFER_LEN) {
595*44704f69SBart Van Assche pr2serr("'--xferlen=%d is out of range ( want <= %d)\n",
596*44704f69SBart Van Assche op->xfer_len, MAX_XFER_LEN);
597*44704f69SBart Van Assche ret = SG_LIB_SYNTAX_ERROR;
598*44704f69SBart Van Assche goto err_out;
599*44704f69SBart Van Assche }
600*44704f69SBart Van Assche wBuff = (uint8_t *)sg_memalign(op->xfer_len, 0, &free_wBuff, false);
601*44704f69SBart Van Assche if (NULL == wBuff) {
602*44704f69SBart Van Assche pr2serr("unable to allocate %d bytes of memory with "
603*44704f69SBart Van Assche "sg_memalign()\n", op->xfer_len);
604*44704f69SBart Van Assche ret = sg_convert_errno(ENOMEM);
605*44704f69SBart Van Assche goto err_out;
606*44704f69SBart Van Assche }
607*44704f69SBart Van Assche if (op->ff)
608*44704f69SBart Van Assche memset(wBuff, 0xff, op->xfer_len);
609*44704f69SBart Van Assche if (op->ifilename[0]) {
610*44704f69SBart Van Assche if (got_stdin) {
611*44704f69SBart Van Assche infd = STDIN_FILENO;
612*44704f69SBart Van Assche if (sg_set_binary_mode(STDIN_FILENO) < 0)
613*44704f69SBart Van Assche perror("sg_set_binary_mode");
614*44704f69SBart Van Assche } else {
615*44704f69SBart Van Assche if ((infd = open(op->ifilename, O_RDONLY)) < 0) {
616*44704f69SBart Van Assche ret = sg_convert_errno(errno);
617*44704f69SBart Van Assche snprintf(ebuff, EBUFF_SZ, ME "could not open %.400s for "
618*44704f69SBart Van Assche "reading", op->ifilename);
619*44704f69SBart Van Assche perror(ebuff);
620*44704f69SBart Van Assche goto err_out;
621*44704f69SBart Van Assche } else if (sg_set_binary_mode(infd) < 0)
622*44704f69SBart Van Assche perror("sg_set_binary_mode");
623*44704f69SBart Van Assche }
624*44704f69SBart Van Assche res = read(infd, wBuff, op->xfer_len);
625*44704f69SBart Van Assche if (res < 0) {
626*44704f69SBart Van Assche ret = sg_convert_errno(errno);
627*44704f69SBart Van Assche snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %.400s",
628*44704f69SBart Van Assche op->ifilename);
629*44704f69SBart Van Assche perror(ebuff);
630*44704f69SBart Van Assche if (! got_stdin)
631*44704f69SBart Van Assche close(infd);
632*44704f69SBart Van Assche goto err_out;
633*44704f69SBart Van Assche }
634*44704f69SBart Van Assche if (res < op->xfer_len) {
635*44704f69SBart Van Assche pr2serr("tried to read %d bytes from %s, got %d bytes\n",
636*44704f69SBart Van Assche op->xfer_len, op->ifilename, res);
637*44704f69SBart Van Assche pr2serr(" so pad with 0x0 bytes and continue\n");
638*44704f69SBart Van Assche }
639*44704f69SBart Van Assche if (! got_stdin)
640*44704f69SBart Van Assche close(infd);
641*44704f69SBart Van Assche } else {
642*44704f69SBart Van Assche if (vb)
643*44704f69SBart Van Assche pr2serr("Default data-out buffer set to %d zeros\n",
644*44704f69SBart Van Assche op->xfer_len);
645*44704f69SBart Van Assche if (prot_en && (op->wrprotect > 0)) {
646*44704f69SBart Van Assche /* default for protection is 0xff, rest get 0x0 */
647*44704f69SBart Van Assche memset(wBuff + op->xfer_len - 8, 0xff, 8);
648*44704f69SBart Van Assche if (vb)
649*44704f69SBart Van Assche pr2serr(" ... apart from last 8 bytes which are set to "
650*44704f69SBart Van Assche "0xff\n");
651*44704f69SBart Van Assche }
652*44704f69SBart Van Assche }
653*44704f69SBart Van Assche }
654*44704f69SBart Van Assche
655*44704f69SBart Van Assche ret = do_write_same(sg_fd, op, wBuff, &act_cdb_len);
656*44704f69SBart Van Assche if (ret) {
657*44704f69SBart Van Assche sg_get_category_sense_str(ret, sizeof(b), b, vb);
658*44704f69SBart Van Assche pr2serr("Write same(%d): %s\n", act_cdb_len, b);
659*44704f69SBart Van Assche }
660*44704f69SBart Van Assche
661*44704f69SBart Van Assche err_out:
662*44704f69SBart Van Assche if (free_wBuff)
663*44704f69SBart Van Assche free(free_wBuff);
664*44704f69SBart Van Assche if (sg_fd >= 0) {
665*44704f69SBart Van Assche res = sg_cmds_close_device(sg_fd);
666*44704f69SBart Van Assche if (res < 0) {
667*44704f69SBart Van Assche pr2serr("close error: %s\n", safe_strerror(-res));
668*44704f69SBart Van Assche if (0 == ret)
669*44704f69SBart Van Assche ret = sg_convert_errno(-res);
670*44704f69SBart Van Assche }
671*44704f69SBart Van Assche }
672*44704f69SBart Van Assche if (0 == op->verbose) {
673*44704f69SBart Van Assche if (! sg_if_can2stderr("sg_write_same failed: ", ret))
674*44704f69SBart Van Assche pr2serr("Some error occurred, try again with '-v' "
675*44704f69SBart Van Assche "or '-vv' for more information\n");
676*44704f69SBart Van Assche }
677*44704f69SBart Van Assche return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
678*44704f69SBart Van Assche }
679