blob: 47c3d8c393f5a4b63d1c9ba97fd4d37066826489 [file] [log] [blame]
/*
* Copyright (c) 2004-2015 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_cmds_basic.h"
#include "sg_cmds_extra.h"
#include "sg_pr2serr.h"
/* A utility program for the Linux OS SCSI subsystem.
*
* This program issues the SCSI VERIFY(10) or VERIFY(16) command to the given
* SCSI block device.
*
* N.B. This utility should, but doesn't, check the logical block size with
* the SCSI READ CAPACITY command. It is up to the user to make sure that
* the count of blocks requested and the number of bytes transferred (when
* BYTCHK>0) are "in sync". That caclculation is somewhat complicated by
* the possibility of protection data (DIF).
*/
static const char * version_str = "1.21 20140516"; /* sbc4r01 */
#define ME "sg_verify: "
#define EBUFF_SZ 256
static struct option long_options[] = {
{"16", no_argument, 0, 'S'},
{"bpc", required_argument, 0, 'b'},
{"bytchk", required_argument, 0, 'B'},
{"count", required_argument, 0, 'c'},
{"dpo", no_argument, 0, 'd'},
{"ebytchk", required_argument, 0, 'E'},
{"group", required_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
{"in", required_argument, 0, 'i'},
{"lba", required_argument, 0, 'l'},
{"nbo", required_argument, 0, 'n'},
{"quiet", no_argument, 0, 'q'},
{"readonly", no_argument, 0, 'r'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"vrprotect", required_argument, 0, 'P'},
{0, 0, 0, 0},
};
static void
usage()
{
pr2serr("Usage: sg_verify [--16] [--bpc=BPC] [--count=COUNT] [--dpo] "
"[--ebytchk=BCH]\n"
" [--group=GN] [--help] [--in=IF] "
"[--lba=LBA] [--ndo=NDO]\n"
" [--quiet] [--readonly] [--verbose] "
"[--version]\n"
" [--vrprotect=VRP] DEVICE\n"
" where:\n"
" --16|-S use VERIFY(16) (def: use "
"VERIFY(10) )\n"
" --bpc=BPC|-b BPC max blocks per verify command "
"(def: 128)\n"
" --count=COUNT|-c COUNT count of blocks to verify "
"(def: 1).\n"
" If BCH=3 then COUNT must "
"be 1 .\n"
" --dpo|-d disable page out (cache retention "
"priority)\n"
" --ebytchk=BCH|-E BCH sets BYTCHK value, either 1, 2 "
"or 3 (def: 0).\n"
" BCH overrides BYTCHK=1 set by "
"'--ndo='. If\n"
" BCH is 3 then NDO must be the LBA "
"size\n"
" (plus protection size if DIF "
"active)\n"
" --group=GN|-g GN set group number field to GN (def: 0)\n"
" --help|-h print out usage message\n"
" --in=IF|-i IF input from file called IF (def: "
"stdin)\n"
" only active if --bytchk=N given\n"
" --lba=LBA|-l LBA logical block address to start "
"verify (def: 0)\n"
" --ndo=NDO|-n NDO NDO is number of bytes placed in "
"data-out buffer.\n"
" These are fetched from IF (or "
"stdin) and used\n"
" to verify the device data against. "
"Forces\n"
" --bpc=COUNT. Sets BYTCHK (byte check) "
"to 1\n"
" --quiet|-q suppress miscompare report to stderr, "
"still\n"
" causes an exit status of 14\n"
" --readonly|-r open DEVICE read-only (def: open it "
"read-write)\n"
" --verbose|-v increase verbosity\n"
" --version|-V print version string and exit\n"
" --vrprotect=VRP|-P VRP set vrprotect field to VRP "
"(def: 0)\n"
"Performs one or more SCSI VERIFY(10) or SCSI VERIFY(16) "
"commands. sbc3r34\nmade the BYTCHK field two bits wide "
"(it was a single bit).\n");
}
int
main(int argc, char * argv[])
{
int sg_fd, res, c, num, nread, infd;
int64_t ll;
int dpo = 0;
int bytchk = 0;
int ndo = 0;
char *ref_data = NULL;
int vrprotect = 0;
int64_t count = 1;
int64_t orig_count;
int bpc = 128;
int bpc_given = 0;
int got_stdin = 0;
int group = 0;
uint64_t lba = 0;
uint64_t orig_lba;
int quiet = 0;
int readonly = 0;
int verbose = 0;
int verify16 = 0;
const char * device_name = NULL;
const char * file_name = NULL;
const char * vc;
int ret = 0;
unsigned int info = 0;
uint64_t info64 = 0;
char ebuff[EBUFF_SZ];
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "b:B:c:dE:g:hi:l:n:P:qrSvV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
case 'b':
bpc = sg_get_num(optarg);
if (bpc < 1) {
pr2serr("bad argument to '--bpc'\n");
return SG_LIB_SYNTAX_ERROR;
}
++bpc_given;
break;
case 'c':
count = sg_get_llnum(optarg);
if (count < 0) {
pr2serr("bad argument to '--count'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'd':
dpo = 1;
break;
case 'E':
bytchk = sg_get_num(optarg);
if ((bytchk < 1) || (bytchk > 3)) {
pr2serr("bad argument to '--ebytchk'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'g':
group = sg_get_num(optarg);
if ((group < 0) || (group > 31)) {
pr2serr("bad argument to '--group'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'h':
case '?':
usage();
return 0;
case 'i':
file_name = optarg;
break;
case 'l':
ll = sg_get_llnum(optarg);
if (-1 == ll) {
pr2serr("bad argument to '--lba'\n");
return SG_LIB_SYNTAX_ERROR;
}
lba = (uint64_t)ll;
break;
case 'n':
case 'B': /* undocumented, old --bytchk=NDO option */
ndo = sg_get_num(optarg);
if (ndo < 1) {
pr2serr("bad argument to '--ndo'\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'P':
vrprotect = sg_get_num(optarg);
if (-1 == vrprotect) {
pr2serr("bad argument to '--vrprotect'\n");
return SG_LIB_SYNTAX_ERROR;
}
if ((vrprotect < 0) || (vrprotect > 7)) {
pr2serr("'--vrprotect' requires a value from 0 to 7 "
"(inclusive)\n");
return SG_LIB_SYNTAX_ERROR;
}
break;
case 'q':
++quiet;
break;
case 'r':
++readonly;
break;
case 'S':
++verify16;
break;
case 'v':
++verbose;
break;
case 'V':
pr2serr(ME "version: %s\n", version_str);
return 0;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (optind < argc) {
if (NULL == device_name) {
device_name = argv[optind];
++optind;
}
if (optind < argc) {
for (; optind < argc; ++optind)
pr2serr("Unexpected extra argument: %s\n", argv[optind]);
usage();
return SG_LIB_SYNTAX_ERROR;
}
}
if (ndo > 0) {
if (0 == bytchk)
bytchk = 1;
if (bpc_given && (bpc != count))
pr2serr("'bpc' argument ignored, using --bpc=%" PRIu64 "\n",
count);
if (count > 0x7fffffffLL) {
pr2serr("count exceed 31 bits, way too large\n");
return SG_LIB_SYNTAX_ERROR;
}
if ((3 == bytchk) && (1 != count)) {
pr2serr("count must be 1 when bytchk=3\n");
return SG_LIB_SYNTAX_ERROR;
}
bpc = (int)count;
} else if (bytchk > 0) {
pr2serr("when the 'ebytchk=BCH' option is given, then '--bytchk=NDO' "
"must also be given\n");
return SG_LIB_SYNTAX_ERROR;
}
if ((bpc > 0xffff) && (0 == verify16)) {
pr2serr("'%s' exceeds 65535, so use VERIFY(16)\n",
(ndo > 0) ? "count" : "bpc");
++verify16;
}
if (((lba + count - 1) > 0xffffffffLLU) && (0 == verify16)) {
pr2serr("'lba' exceed 32 bits, so use VERIFY(16)\n");
++verify16;
}
if ((group > 0) && (0 == verify16))
pr2serr("group number ignored with VERIFY(10) command, use the --16 "
"option\n");
orig_count = count;
orig_lba = lba;
if (ndo > 0) {
ref_data = (char *)malloc(ndo);
if (NULL == ref_data) {
pr2serr("failed to allocate %d byte buffer\n", ndo);
return SG_LIB_FILE_ERROR;
}
if ((NULL == file_name) || (0 == strcmp(file_name, "-"))) {
++got_stdin;
infd = STDIN_FILENO;
if (sg_set_binary_mode(STDIN_FILENO) < 0)
perror("sg_set_binary_mode");
} else {
if ((infd = open(file_name, O_RDONLY)) < 0) {
snprintf(ebuff, EBUFF_SZ,
ME "could not open %s for reading", file_name);
perror(ebuff);
ret = SG_LIB_FILE_ERROR;
goto err_out;
} else if (sg_set_binary_mode(infd) < 0)
perror("sg_set_binary_mode");
}
if (verbose && got_stdin)
pr2serr("about to wait on STDIN\n");
for (nread = 0; nread < ndo; nread += res) {
res = read(infd, ref_data + nread, ndo - nread);
if (res <= 0) {
pr2serr("reading from %s failed at file offset=%d\n",
(got_stdin ? "stdin" : file_name), nread);
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
}
if (! got_stdin)
close(infd);
}
if (NULL == device_name) {
pr2serr("missing device name!\n");
usage();
ret = SG_LIB_SYNTAX_ERROR;
goto err_out;
}
sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
if (sg_fd < 0) {
pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
ret = SG_LIB_FILE_ERROR;
goto err_out;
}
vc = verify16 ? "VERIFY(16)" : "VERIFY(10)";
for (; count > 0; count -= bpc, lba += bpc) {
num = (count > bpc) ? bpc : count;
if (verify16)
res = sg_ll_verify16(sg_fd, vrprotect, dpo, bytchk,
lba, num, group, ref_data,
ndo, &info64, !quiet , verbose);
else
res = sg_ll_verify10(sg_fd, vrprotect, dpo, bytchk,
(unsigned int)lba, num, ref_data,
ndo, &info, !quiet, verbose);
if (0 != res) {
char b[80];
ret = res;
switch (res) {
case SG_LIB_CAT_ILLEGAL_REQ:
pr2serr("bad field in %s cdb, near lba=0x%" PRIx64 "\n", vc,
lba);
break;
case SG_LIB_CAT_MEDIUM_HARD:
pr2serr("%s medium or hardware error near lba=0x%" PRIx64 "\n",
vc, lba);
break;
case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
if (verify16)
pr2serr("%s medium or hardware error, reported lba=0x%"
PRIx64 "\n", vc, info64);
else
pr2serr("%s medium or hardware error, reported lba=0x%x\n",
vc, info);
break;
case SG_LIB_CAT_MISCOMPARE:
if ((0 == quiet) || verbose)
pr2serr("%s reported MISCOMPARE\n", vc);
break;
default:
sg_get_category_sense_str(res, sizeof(b), b, verbose);
pr2serr("%s: %s\n", vc, b);
pr2serr(" failed near lba=%" PRIu64 " [0x%" PRIx64 "]\n",
lba, lba);
break;
}
break;
}
}
if (verbose && (0 == ret) && (orig_count > 1))
pr2serr("Verified %" PRId64 " [0x%" PRIx64 "] blocks from lba %" PRIu64
" [0x%" PRIx64 "]\n without error\n", orig_count,
(uint64_t)orig_count, orig_lba, orig_lba);
res = sg_cmds_close_device(sg_fd);
if (res < 0) {
pr2serr("close error: %s\n", safe_strerror(-res));
if (0 == ret)
ret = SG_LIB_FILE_ERROR;
}
err_out:
if (ref_data)
free(ref_data);
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
}