xref: /aosp_15_r20/external/sg3_utils/src/sg_verify.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2004-2020 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  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "sg_lib.h"
26 #include "sg_cmds_basic.h"
27 #include "sg_cmds_extra.h"
28 #include "sg_pr2serr.h"
29 
30 /* A utility program for the Linux OS SCSI subsystem.
31  *
32  * This program issues the SCSI VERIFY(10) or VERIFY(16) command to the given
33  * SCSI block device.
34  *
35  * N.B. This utility should, but doesn't, check the logical block size with
36  * the SCSI READ CAPACITY command. It is up to the user to make sure that
37  * the count of blocks requested and the number of bytes transferred (when
38  * BYTCHK>0) are "in sync". That caclculation is somewhat complicated by
39  * the possibility of protection data (DIF).
40  */
41 
42 static const char * version_str = "1.27 20201029";    /* sbc4r17 */
43 
44 #define ME "sg_verify: "
45 
46 #define EBUFF_SZ 256
47 
48 
49 static struct option long_options[] = {
50         {"0", no_argument, 0, '0'},
51         {"16", no_argument, 0, 'S'},
52         {"bpc", required_argument, 0, 'b'},
53         {"bytchk", required_argument, 0, 'B'},  /* 4 backward compatibility */
54         {"count", required_argument, 0, 'c'},
55         {"dpo", no_argument, 0, 'd'},
56         {"ebytchk", required_argument, 0, 'E'}, /* extended bytchk (2 bits) */
57         {"group", required_argument, 0, 'g'},
58         {"help", no_argument, 0, 'h'},
59         {"in", required_argument, 0, 'i'},
60         {"lba", required_argument, 0, 'l'},
61         {"nbo", required_argument, 0, 'n'},     /* misspelling, legacy */
62         {"ndo", required_argument, 0, 'n'},
63         {"quiet", no_argument, 0, 'q'},
64         {"readonly", no_argument, 0, 'r'},
65         {"verbose", no_argument, 0, 'v'},
66         {"version", no_argument, 0, 'V'},
67         {"vrprotect", required_argument, 0, 'P'},
68         {0, 0, 0, 0},
69 };
70 
71 static void
usage()72 usage()
73 {
74     pr2serr("Usage: sg_verify [--0] [--16] [--bpc=BPC] [--count=COUNT] "
75             "[--dpo]\n"
76             "                 [--ebytchk=BCH] [--ff] [--group=GN] [--help] "
77             "[--in=IF]\n"
78             "                 [--lba=LBA] [--ndo=NDO] [--quiet] "
79             "[--readonly]\n"
80             "                 [--verbose] [--version] [--vrprotect=VRP] "
81             "DEVICE\n"
82             "  where:\n"
83             "    --0|-0              fill buffer with zeros (don't read "
84             "stdin)\n"
85             "    --16|-S             use VERIFY(16) (def: use "
86             "VERIFY(10) )\n"
87             "    --bpc=BPC|-b BPC    max blocks per verify command "
88             "(def: 128)\n"
89             "    --count=COUNT|-c COUNT    count of blocks to verify "
90             "(def: 1).\n"
91             "    --dpo|-d            disable page out (cache retention "
92             "priority)\n"
93             "    --ebytchk=BCH|-E BCH    sets BYTCHK value, either 1, 2 "
94             "or 3 (def: 0).\n"
95             "                            BCH overrides BYTCHK=1 set by "
96             "'--ndo='. If\n"
97             "                            BCH is 3 then NDO must be the LBA "
98             "size\n"
99             "                            (plus protection size if DIF "
100             "active)\n"
101             "    --ff|-f             fill buffer with 0xff bytes (don't read "
102             "stdin)\n"
103             "    --group=GN|-g GN    set group number field to GN (def: 0)\n"
104             "    --help|-h           print out usage message\n"
105             "    --in=IF|-i IF       input from file called IF (def: "
106             "stdin)\n"
107             "                        only active if --ebytchk=BCH given\n"
108             "    --lba=LBA|-l LBA    logical block address to start "
109             "verify (def: 0)\n"
110             "    --ndo=NDO|-n NDO    NDO is number of bytes placed in "
111             "data-out buffer.\n"
112             "                        These are fetched from IF (or "
113             "stdin) and used\n"
114             "                        to verify the device data against. "
115             "Forces\n"
116             "                        --bpc=COUNT. Sets BYTCHK (byte check) "
117             "to 1\n"
118             "    --quiet|-q          suppress miscompare report to stderr, "
119             "still\n"
120             "                        causes an exit status of 14\n"
121             "    --readonly|-r       open DEVICE read-only (def: open it "
122             "read-write)\n"
123             "    --verbose|-v        increase verbosity\n"
124             "    --version|-V        print version string and exit\n"
125             "    --vrprotect=VRP|-P VRP    set vrprotect field to VRP "
126             "(def: 0)\n\n"
127             "Performs one or more SCSI VERIFY(10) or SCSI VERIFY(16) "
128             "commands. sbc3r34\nmade the BYTCHK field two bits wide "
129             "(it was a single bit).\n");
130 }
131 
132 int
main(int argc,char * argv[])133 main(int argc, char * argv[])
134 {
135     bool bpc_given = false;
136     bool dpo = false;
137     bool ff_given = false;
138     bool got_stdin = false;
139     bool quiet = false;
140     bool readonly = false;
141     bool verbose_given = false;
142     bool verify16 = false;
143     bool version_given = false;
144     bool zero_given = false;
145     int res, c, num, nread, infd;
146     int sg_fd = -1;
147     int bpc = 128;
148     int group = 0;
149     int bytchk = 0;
150     int ndo = 0;        /* number of bytes in data-out buffer */
151     int verbose = 0;
152     int ret = 0;
153     int vrprotect = 0;
154     unsigned int info = 0;
155     int64_t count = 1;
156     int64_t ll;
157     int64_t orig_count;
158     uint64_t info64 = 0;
159     uint64_t lba = 0;
160     uint64_t orig_lba;
161     uint8_t * ref_data = NULL;
162     uint8_t * free_ref_data = NULL;
163     const char * device_name = NULL;
164     const char * file_name = NULL;
165     const char * vc;
166     char ebuff[EBUFF_SZ];
167 
168     while (1) {
169         int option_index = 0;
170 
171         c = getopt_long(argc, argv, "0b:B:c:dE:fg:hi:l:n:P:qrSvV",
172                         long_options, &option_index);
173         if (c == -1)
174             break;
175 
176         switch (c) {
177         case '0':
178             zero_given = true;
179             break;
180         case 'b':
181             bpc = sg_get_num(optarg);
182             if (bpc < 1) {
183                 pr2serr("bad argument to '--bpc'\n");
184                 return SG_LIB_SYNTAX_ERROR;
185             }
186             bpc_given = true;
187             break;
188         case 'c':
189             count = sg_get_llnum(optarg);
190             if (count < 0) {
191                 pr2serr("bad argument to '--count'\n");
192                 return SG_LIB_SYNTAX_ERROR;
193             }
194             break;
195         case 'd':
196             dpo = true;
197             break;
198         case 'E':
199             bytchk = sg_get_num(optarg);
200             if ((bytchk < 0) || (bytchk > 3)) {
201                 pr2serr("bad argument to '--ebytchk'\n");
202                 return SG_LIB_SYNTAX_ERROR;
203             }
204             break;
205         case 'f':
206             ff_given = true;
207             break;
208         case 'g':
209             group = sg_get_num(optarg);
210             if ((group < 0) || (group > 63)) {
211                 pr2serr("bad argument to '--group'\n");
212                 return SG_LIB_SYNTAX_ERROR;
213             }
214             break;
215         case 'h':
216         case '?':
217             usage();
218             return 0;
219         case 'i':
220             file_name = optarg;
221             break;
222         case 'l':
223             ll = sg_get_llnum(optarg);
224             if (-1 == ll) {
225                 pr2serr("bad argument to '--lba'\n");
226                 return SG_LIB_SYNTAX_ERROR;
227             }
228             lba = (uint64_t)ll;
229             break;
230         case 'n':       /* number of bytes in data-out buffer */
231         case 'B':       /* undocumented, old --bytchk=NDO option */
232             ndo = sg_get_num(optarg);
233             if (ndo < 1) {
234                 pr2serr("bad argument to '--ndo'\n");
235                 return SG_LIB_SYNTAX_ERROR;
236             }
237             break;
238         case 'P':
239             vrprotect = sg_get_num(optarg);
240             if (-1 == vrprotect) {
241                 pr2serr("bad argument to '--vrprotect'\n");
242                 return SG_LIB_SYNTAX_ERROR;
243             }
244             if ((vrprotect < 0) || (vrprotect > 7)) {
245                 pr2serr("'--vrprotect' requires a value from 0 to 7 "
246                         "(inclusive)\n");
247                 return SG_LIB_SYNTAX_ERROR;
248             }
249             break;
250         case 'q':
251             quiet = true;
252             break;
253         case 'r':
254             readonly = true;
255             break;
256         case 'S':
257             verify16 = true;
258             break;
259         case 'v':
260             verbose_given = true;
261             ++verbose;
262             break;
263         case 'V':
264             version_given = true;
265             break;
266         default:
267             pr2serr("unrecognised option code 0x%x ??\n", c);
268             usage();
269             return SG_LIB_SYNTAX_ERROR;
270         }
271     }
272     if (optind < argc) {
273         if (NULL == device_name) {
274             device_name = argv[optind];
275             ++optind;
276         }
277         if (optind < argc) {
278             for (; optind < argc; ++optind)
279                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
280             usage();
281             return SG_LIB_SYNTAX_ERROR;
282         }
283     }
284 #ifdef DEBUG
285     pr2serr("In DEBUG mode, ");
286     if (verbose_given && version_given) {
287         pr2serr("but override: '-vV' given, zero verbose and continue\n");
288         verbose_given = false;
289         version_given = false;
290         verbose = 0;
291     } else if (! verbose_given) {
292         pr2serr("set '-vv'\n");
293         verbose = 2;
294     } else
295         pr2serr("keep verbose=%d\n", verbose);
296 #else
297     if (verbose_given && version_given)
298         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
299 #endif
300     if (version_given) {
301         pr2serr(ME "version: %s\n", version_str);
302         return 0;
303     }
304 
305     if (ndo > 0) {
306         if (0 == bytchk)
307             bytchk = 1;
308         if (bpc_given && (bpc != count))
309             pr2serr("'bpc' argument ignored, using --bpc=%" PRIu64 "\n",
310                     count);
311         if (count > 0x7fffffffLL) {
312             pr2serr("count exceed 31 bits, way too large\n");
313             return SG_LIB_SYNTAX_ERROR;
314         }
315 #if 0
316         if ((3 == bytchk) && (1 != count)) {
317             pr2serr("count must be 1 when bytchk=3\n");
318             return SG_LIB_SYNTAX_ERROR;
319         }
320         // bpc = (int)count;
321 #endif
322     } else if (bytchk > 0) {
323         pr2serr("when the 'ebytchk=BCH' option is given, then '--ndo=NDO' "
324                 "must also be given\n");
325         return SG_LIB_CONTRADICT;
326     }
327     if ((zero_given || ff_given) && file_name) {
328         pr2serr("giving --0 or --ff is not compatible with --if=%s\n",
329                 file_name);
330         return SG_LIB_CONTRADICT;
331     }
332 
333     if ((bpc > 0xffff) && (! verify16)) {
334         pr2serr("'%s' exceeds 65535, so use VERIFY(16)\n",
335                 (ndo > 0) ? "count" : "bpc");
336         verify16 = true;
337     }
338     if (((lba + count - 1) > 0xffffffffLLU) && (! verify16)) {
339         pr2serr("'lba' exceed 32 bits, so use VERIFY(16)\n");
340         verify16 = true;
341     }
342     if ((group > 0) && (! verify16))
343         pr2serr("group number ignored with VERIFY(10) command, use the --16 "
344                 "option\n");
345 
346     orig_count = count;
347     orig_lba = lba;
348 
349     if (ndo > 0) {
350         ref_data = (uint8_t *)sg_memalign(ndo, 0, &free_ref_data, verbose > 4);
351         if (NULL == ref_data) {
352             pr2serr("failed to allocate %d byte buffer\n", ndo);
353             ret = sg_convert_errno(ENOMEM);
354             goto err_out;
355         }
356         if (ff_given)
357             memset(ref_data, 0xff, ndo);
358         if (zero_given || ff_given)
359             goto skip;
360         if ((NULL == file_name) || (0 == strcmp(file_name, "-"))) {
361             got_stdin = true;
362             infd = STDIN_FILENO;
363             if (sg_set_binary_mode(STDIN_FILENO) < 0)
364                 perror("sg_set_binary_mode");
365         } else {
366             if ((infd = open(file_name, O_RDONLY)) < 0) {
367                 ret = sg_convert_errno(errno);
368                 snprintf(ebuff, EBUFF_SZ,
369                          ME "could not open %s for reading", file_name);
370                 perror(ebuff);
371                 goto err_out;
372             } else if (sg_set_binary_mode(infd) < 0)
373                 perror("sg_set_binary_mode");
374         }
375         if (verbose && got_stdin)
376                 pr2serr("about to wait on STDIN\n");
377         for (nread = 0; nread < ndo; nread += res) {
378             res = read(infd, ref_data + nread, ndo - nread);
379             if (res <= 0) {
380                 ret = sg_convert_errno(errno);
381                 pr2serr("reading from %s failed at file offset=%d\n",
382                         (got_stdin ? "stdin" : file_name), nread);
383                 goto err_out;
384             }
385         }
386         if (! got_stdin)
387             close(infd);
388     }
389 skip:
390     if (NULL == device_name) {
391         pr2serr("missing device name!\n\n");
392         usage();
393         ret = SG_LIB_SYNTAX_ERROR;
394         goto err_out;
395     }
396     sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
397     if (sg_fd < 0) {
398         if (verbose)
399             pr2serr(ME "open error: %s: %s\n", device_name,
400                     safe_strerror(-sg_fd));
401         ret = sg_convert_errno(-sg_fd);
402         goto err_out;
403     }
404 
405     vc = verify16 ? "VERIFY(16)" : "VERIFY(10)";
406     for (; count > 0; count -= bpc, lba += bpc) {
407         num = (count > bpc) ? bpc : count;
408         if (verify16)
409             res = sg_ll_verify16(sg_fd, vrprotect, dpo, bytchk,
410                                  lba, num, group, ref_data,
411                                  ndo, &info64, !quiet , verbose);
412         else
413             res = sg_ll_verify10(sg_fd, vrprotect, dpo, bytchk,
414                                  (unsigned int)lba, num, ref_data,
415                                  ndo, &info, !quiet, verbose);
416         if (0 != res) {
417             char b[80];
418 
419             ret = res;
420             switch (res) {
421             case SG_LIB_CAT_ILLEGAL_REQ:
422                 pr2serr("bad field in %s cdb, near lba=0x%" PRIx64 "\n", vc,
423                         lba);
424                 break;
425             case SG_LIB_CAT_MEDIUM_HARD:
426                 pr2serr("%s medium or hardware error near lba=0x%" PRIx64 "\n",
427                         vc, lba);
428                 break;
429             case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
430                 if (verify16)
431                     pr2serr("%s medium or hardware error, reported lba=0x%"
432                             PRIx64 "\n", vc, info64);
433                 else
434                     pr2serr("%s medium or hardware error, reported lba=0x%x\n",
435                             vc, info);
436                 break;
437             case SG_LIB_CAT_MISCOMPARE:
438                 if ((0 == quiet) || verbose)
439                     pr2serr("%s MISCOMPARE: started at LBA 0x%" PRIx64 "\n",
440                             vc, lba);
441                 break;
442             default:
443                 sg_get_category_sense_str(res, sizeof(b), b, verbose);
444                 pr2serr("%s: %s\n", vc, b);
445                 pr2serr("    failed near lba=%" PRIu64 " [0x%" PRIx64 "]\n",
446                         lba, lba);
447                 break;
448             }
449             break;
450         }
451     }
452 
453     if (verbose && (0 == ret) && (orig_count > 1))
454         pr2serr("Verified %" PRId64 " [0x%" PRIx64 "] blocks from lba %" PRIu64
455                 " [0x%" PRIx64 "]\n    without error\n", orig_count,
456                 (uint64_t)orig_count, orig_lba, orig_lba);
457 
458  err_out:
459     if (sg_fd >= 0) {
460         res = sg_cmds_close_device(sg_fd);
461         if (res < 0) {
462             pr2serr("close error: %s\n", safe_strerror(-res));
463             if (0 == ret)
464                 ret = sg_convert_errno(-res);
465         }
466     }
467     if (free_ref_data)
468         free(free_ref_data);
469     if (0 == verbose) {
470         if (! sg_if_can2stderr("sg_verify failed: ", ret))
471             pr2serr("Some error occurred, try again with '-v' "
472                     "or '-vv' for more information\n");
473     }
474     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
475 }
476