xref: /aosp_15_r20/external/sg3_utils/src/sg_write_long.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* A utility program for the Linux OS SCSI subsystem.
2  *  Copyright (C) 2004-2018 D. Gilbert
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2, or (at your option)
6  *  any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  * This program issues the SCSI command WRITE LONG to a given SCSI device.
11  * It sends the command with the logical block address passed as the lba
12  * argument, and the transfer length set to the xfer_len argument. the
13  * buffer to be written to the device filled with 0xff, this buffer includes
14  * the sector data and the ECC bytes.
15  *
16  * This code was contributed by Saeed Bishara
17  */
18 
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #define __STDC_FORMAT_MACROS 1
29 #include <inttypes.h>
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include "sg_lib.h"
36 #include "sg_cmds_basic.h"
37 #include "sg_cmds_extra.h"
38 #include "sg_pr2serr.h"
39 
40 static const char * version_str = "1.21 20180723";
41 
42 
43 #define MAX_XFER_LEN (15 * 1024)
44 
45 #define ME "sg_write_long: "
46 
47 #define EBUFF_SZ 512
48 
49 static struct option long_options[] = {
50         {"16", no_argument, 0, 'S'},
51         {"cor_dis", no_argument, 0, 'c'},
52         {"cor-dis", no_argument, 0, 'c'},
53         {"help", no_argument, 0, 'h'},
54         {"in", required_argument, 0, 'i'},
55         {"lba", required_argument, 0, 'l'},
56         {"pblock", no_argument, 0, 'p'},
57         {"verbose", no_argument, 0, 'v'},
58         {"version", no_argument, 0, 'V'},
59         {"wr_uncor", no_argument, 0, 'w'},
60         {"wr-uncor", no_argument, 0, 'w'},
61         {"xfer_len", required_argument, 0, 'x'},
62         {"xfer-len", required_argument, 0, 'x'},
63         {0, 0, 0, 0},
64 };
65 
66 
67 
68 static void
usage()69 usage()
70 {
71   pr2serr("Usage: sg_write_long [--16] [--cor_dis] [--help] [--in=IF] "
72           "[--lba=LBA]\n"
73           "                     [--pblock] [--verbose] [--version] "
74           "[--wr_uncor]\n"
75           "                     [--xfer_len=BTL] DEVICE\n"
76           "  where:\n"
77           "    --16|-S              do WRITE LONG(16) (default: 10)\n"
78           "    --cor_dis|-c         set correction disabled bit\n"
79           "    --help|-h            print out usage message\n"
80           "    --in=IF|-i IF        input from file called IF (default: "
81           "use\n"
82           "                         0xff bytes as fill)\n"
83           "    --lba=LBA|-l LBA     logical block address "
84           "(default: 0)\n"
85           "    --pblock|-p          physical block (default: logical "
86           "block)\n"
87           "    --verbose|-v         increase verbosity\n"
88           "    --version|-V         print version string then exit\n"
89           "    --wr_uncor|-w        set an uncorrectable error (no "
90           "data transferred)\n"
91           "    --xfer_len=BTL|-x BTL    byte transfer length (< 10000) "
92           "(default:\n"
93           "                             520 bytes)\n\n"
94           "Performs a SCSI WRITE LONG (10 or 16) command. Writes a single "
95           "block\nincluding associated ECC data. That data may be obtained "
96           "from the\nSCSI READ LONG command. See the sg_read_long utility.\n"
97           );
98 }
99 
100 int
main(int argc,char * argv[])101 main(int argc, char * argv[])
102 {
103     bool do_16 = false;
104     bool cor_dis = false;
105     bool got_stdin;
106     bool pblock = false;
107     bool verbose_given = false;
108     bool version_given = false;
109     bool wr_uncor = false;
110     int res, c, infd, offset;
111     int sg_fd = -1;
112     int xfer_len = 520;
113     int ret = 1;
114     int verbose = 0;
115     int64_t ll;
116     uint64_t llba = 0;
117     const char * device_name = NULL;
118     uint8_t * writeLongBuff = NULL;
119     void * rawp = NULL;
120     uint8_t * free_rawp = NULL;
121     const char * ten_or;
122     char file_name[256];
123     char b[80];
124     char ebuff[EBUFF_SZ];
125 
126     memset(file_name, 0, sizeof file_name);
127     while (1) {
128         int option_index = 0;
129 
130         c = getopt_long(argc, argv, "chi:l:pSvVwx:", long_options,
131                         &option_index);
132         if (c == -1)
133             break;
134 
135         switch (c) {
136         case 'c':
137             cor_dis = true;
138             break;
139         case 'h':
140         case '?':
141             usage();
142             return 0;
143         case 'i':
144             strncpy(file_name, optarg, sizeof(file_name) - 1);
145             file_name[sizeof(file_name) - 1] = '\0';
146             break;
147         case 'l':
148             ll = sg_get_llnum(optarg);
149             if (-1 == ll) {
150                 pr2serr("bad argument to '--lba'\n");
151                 return SG_LIB_SYNTAX_ERROR;
152             }
153             llba = (uint64_t)ll;
154             break;
155         case 'p':
156             pblock = true;
157             break;
158         case 'S':
159             do_16 = true;
160             break;
161         case 'v':
162             verbose_given = true;
163             ++verbose;
164             break;
165         case 'V':
166             version_given = true;
167             break;
168         case 'w':
169             wr_uncor = true;
170             break;
171         case 'x':
172             xfer_len = sg_get_num(optarg);
173             if (-1 == xfer_len) {
174                 pr2serr("bad argument to '--xfer_len'\n");
175                 return SG_LIB_SYNTAX_ERROR;
176             }
177             break;
178         default:
179             pr2serr("unrecognised option code 0x%x ??\n", c);
180             usage();
181             return SG_LIB_SYNTAX_ERROR;
182         }
183     }
184     if (optind < argc) {
185         if (NULL == device_name) {
186             device_name = argv[optind];
187             ++optind;
188         }
189         if (optind < argc) {
190             for (; optind < argc; ++optind)
191                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
192             usage();
193             return SG_LIB_SYNTAX_ERROR;
194         }
195     }
196 
197 #ifdef DEBUG
198     pr2serr("In DEBUG mode, ");
199     if (verbose_given && version_given) {
200         pr2serr("but override: '-vV' given, zero verbose and continue\n");
201         verbose_given = false;
202         version_given = false;
203         verbose = 0;
204     } else if (! verbose_given) {
205         pr2serr("set '-vv'\n");
206         verbose = 2;
207     } else
208         pr2serr("keep verbose=%d\n", verbose);
209 #else
210     if (verbose_given && version_given)
211         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
212 #endif
213     if (version_given) {
214         pr2serr(ME "version: %s\n", version_str);
215         return 0;
216     }
217 
218     if (NULL == device_name) {
219         pr2serr("Missing device name!\n\n");
220         usage();
221         return SG_LIB_SYNTAX_ERROR;
222     }
223     if (wr_uncor)
224         xfer_len = 0;
225     else if (xfer_len >= MAX_XFER_LEN) {
226         pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
227                 MAX_XFER_LEN);
228         usage();
229         return SG_LIB_SYNTAX_ERROR;
230     }
231     sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
232     if (sg_fd < 0) {
233         if (verbose)
234             pr2serr(ME "open error: %s: %s\n", device_name,
235                     safe_strerror(-sg_fd));
236         ret = sg_convert_errno(-sg_fd);
237         goto err_out;
238     }
239 
240     if (wr_uncor) {
241         if ('\0' != file_name[0])
242             pr2serr(">>> warning: when '--wr_uncor' given '-in=' is "
243                     "ignored\n");
244     } else {
245         if (NULL == (rawp = sg_memalign(MAX_XFER_LEN, 0, &free_rawp, false))) {
246             pr2serr(ME "out of memory\n");
247             ret = sg_convert_errno(ENOMEM);
248             goto err_out;
249         }
250         writeLongBuff = (uint8_t *)rawp;
251         memset(rawp, 0xff, MAX_XFER_LEN);
252         if (file_name[0]) {
253             got_stdin = (0 == strcmp(file_name, "-"));
254             if (got_stdin) {
255                 infd = STDIN_FILENO;
256                 if (sg_set_binary_mode(STDIN_FILENO) < 0)
257                     perror("sg_set_binary_mode");
258             } else {
259                 if ((infd = open(file_name, O_RDONLY)) < 0) {
260                     snprintf(ebuff, EBUFF_SZ,
261                              ME "could not open %s for reading", file_name);
262                     perror(ebuff);
263                     goto err_out;
264                 } else if (sg_set_binary_mode(infd) < 0)
265                     perror("sg_set_binary_mode");
266             }
267             res = read(infd, writeLongBuff, xfer_len);
268             if (res < 0) {
269                 snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
270                          file_name);
271                 perror(ebuff);
272                 if (! got_stdin)
273                     close(infd);
274                 goto err_out;
275             }
276             if (res < xfer_len) {
277                 pr2serr("tried to read %d bytes from %s, got %d bytes\n",
278                         xfer_len, file_name, res);
279                 pr2serr("pad with 0xff bytes and continue\n");
280             }
281             if (! got_stdin)
282                 close(infd);
283         }
284     }
285     if (verbose)
286         pr2serr(ME "issue write long to device %s\n\t\txfer_len= %d (0x%x), "
287                 "lba=%" PRIu64 " (0x%" PRIx64 ")\n    cor_dis=%d, "
288                 "wr_uncor=%d, pblock=%d\n", device_name, xfer_len, xfer_len,
289                 llba, llba, (int)cor_dis, (int)wr_uncor, (int)pblock);
290 
291     ten_or = do_16 ? "16" : "10";
292     if (do_16)
293         res = sg_ll_write_long16(sg_fd, cor_dis, wr_uncor, pblock, llba,
294                                  writeLongBuff, xfer_len, &offset, true,
295                                  verbose);
296     else
297         res = sg_ll_write_long10(sg_fd, cor_dis, wr_uncor, pblock,
298                                  (unsigned int)llba, writeLongBuff, xfer_len,
299                                  &offset, true, verbose);
300     ret = res;
301     switch (res) {
302     case 0:
303         break;
304     case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
305         pr2serr("<<< device indicates 'xfer_len' should be %d >>>\n",
306                 xfer_len - offset);
307         break;
308     default:
309         sg_get_category_sense_str(res, sizeof(b), b, verbose);
310         pr2serr("  SCSI WRITE LONG (%s): %s\n", ten_or, b);
311         break;
312     }
313 
314 err_out:
315     if (free_rawp)
316         free(free_rawp);
317     if (sg_fd >= 0) {
318         res = sg_cmds_close_device(sg_fd);
319         if (res < 0) {
320             pr2serr("close error: %s\n", safe_strerror(-res));
321             if (0 == ret)
322                 ret = sg_convert_errno(-res);
323         }
324     }
325     if (0 == verbose) {
326         if (! sg_if_can2stderr("sg_write_long failed: ", ret))
327             pr2serr("Some error occurred, try again with '-v' "
328                     "or '-vv' for more information\n");
329     }
330     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
331 }
332