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