aboutsummaryrefslogtreecommitdiff
path: root/src/sg_sanitize.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_sanitize.c')
-rw-r--r--src/sg_sanitize.c792
1 files changed, 792 insertions, 0 deletions
diff --git a/src/sg_sanitize.c b/src/sg_sanitize.c
new file mode 100644
index 00000000..89108fed
--- /dev/null
+++ b/src/sg_sanitize.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2011-2022 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.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.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_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.19 20220608";
+
+#define ME "sg_sanitize: "
+
+#define SANITIZE_OP 0x48
+#define SANITIZE_OP_LEN 10
+#define SANITIZE_SA_OVERWRITE 0x1
+#define SANITIZE_SA_BLOCK_ERASE 0x2
+#define SANITIZE_SA_CRYPTO_ERASE 0x3
+#define SANITIZE_SA_EXIT_FAIL_MODE 0x1f
+#define DEF_REQS_RESP_LEN 252
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define MAX_XFER_LEN 65535
+#define EBUFF_SZ 256
+
+#define SHORT_TIMEOUT 20 /* 20 seconds unless immed=0 ... */
+#define LONG_TIMEOUT (15 * 3600) /* 15 hours ! */
+ /* Seagate ST32000444SS 2TB disk takes 9.5 hours to format */
+#define POLL_DURATION_SECS 60
+
+
+static struct option long_options[] = {
+ {"ause", no_argument, 0, 'A'},
+ {"block", no_argument, 0, 'B'},
+ {"count", required_argument, 0, 'c'},
+ {"crypto", no_argument, 0, 'C'},
+ {"desc", no_argument, 0, 'd'},
+ {"dry-run", no_argument, 0, 'D'},
+ {"dry_run", no_argument, 0, 'D'},
+ {"early", no_argument, 0, 'e'},
+ {"fail", no_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"invert", no_argument, 0, 'I'},
+ {"ipl", required_argument, 0, 'i'},
+ {"overwrite", no_argument, 0, 'O'},
+ {"pattern", required_argument, 0, 'p'},
+ {"quick", no_argument, 0, 'Q'},
+ {"test", required_argument, 0, 'T'},
+ {"timeout", required_argument, 0, 't'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"wait", no_argument, 0, 'w'},
+ {"zero", no_argument, 0, 'z'},
+ {0, 0, 0, 0},
+};
+
+struct opts_t {
+ bool ause;
+ bool block;
+ bool crypto;
+ bool desc;
+ bool dry_run;
+ bool early;
+ bool fail;
+ bool invert;
+ bool overwrite;
+ bool quick;
+ bool verbose_given;
+ bool version_given;
+ bool wait;
+ bool znr;
+ int count;
+ int ipl; /* initialization pattern length */
+ int test;
+ int timeout; /* in seconds */
+ int verbose;
+ int zero;
+ const char * pattern_fn;
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_sanitize [--ause] [--block] [--count=OC] [--crypto] "
+ "[--dry-run]\n"
+ " [--early] [--fail] [--help] [--invert] "
+ "[--ipl=LEN]\n"
+ " [--overwrite] [--pattern=PF] [--quick] "
+ "[--test=TE]\n"
+ " [--timeout=SECS] [--verbose] [--version] "
+ "[--wait]\n"
+ " [--zero] [--znr] DEVICE\n"
+ " where:\n"
+ " --ause|-A set AUSE bit in cdb\n"
+ " --block|-B do BLOCK ERASE sanitize\n"
+ " --count=OC|-c OC OC is overwrite count field (from 1 "
+ "(def) to 31)\n"
+ " --crypto|-C do CRYPTOGRAPHIC ERASE sanitize\n"
+ " --desc|-d polling request sense sets 'desc' "
+ "field\n"
+ " (def: clear 'desc' field)\n"
+ " --dry-run|-D to preparation but bypass SANITIZE "
+ "command\n"
+ " --early|-e exit once sanitize started (IMMED set "
+ "in cdb)\n"
+ " user can monitor progress with REQUEST "
+ "SENSE\n"
+ " --fail|-F do EXIT FAILURE MODE sanitize\n"
+ " --help|-h print out usage message\n"
+ " --invert|-I set INVERT bit in OVERWRITE parameter "
+ "list\n"
+ " --ipl=LEN|-i LEN initialization pattern length (in "
+ "bytes)\n"
+ " --overwrite|-O do OVERWRITE sanitize\n"
+ " --pattern=PF|-p PF PF is file containing initialization "
+ "pattern\n"
+ " for OVERWRITE\n"
+ " --quick|-Q start sanitize without pause for user\n"
+ " intervention (i.e. no time to "
+ "reconsider)\n"
+ " --test=TE|-T TE TE is placed in TEST field of "
+ "OVERWRITE\n"
+ " parameter list (def: 0)\n"
+ " --timeout=SECS|-t SECS SANITIZE command timeout in "
+ "seconds\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string then exit\n"
+ " --wait|-w wait for command to finish (could "
+ "take hours)\n"
+ " --zero|-z use pattern of zeros for "
+ "OVERWRITE\n"
+ " --znr|-Z set ZNR (zone no reset) bit in cdb\n\n"
+ "Performs a SCSI SANITIZE command.\n <<<WARNING>>>: all data "
+ "on DEVICE will be lost.\nDefault action is to give user time to "
+ "reconsider; then execute SANITIZE\ncommand with IMMED bit set; "
+ "then use REQUEST SENSE command every 60\nseconds to poll for a "
+ "progress indication; then exit when there is no\nmore progress "
+ "indication.\n"
+ );
+}
+
+/* Invoke SCSI SANITIZE command. Returns 0 if successful, otherwise error */
+static int
+do_sanitize(int sg_fd, const struct opts_t * op, const void * param_lstp,
+ int param_lst_len)
+{
+ bool immed;
+ int ret, res, sense_cat, timeout;
+ uint8_t san_cdb[SANITIZE_OP_LEN];
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+ struct sg_pt_base * ptvp;
+
+ if (op->early || op->wait)
+ immed = op->early;
+ else
+ immed = true;
+ timeout = (immed ? SHORT_TIMEOUT : LONG_TIMEOUT);
+ /* only use command line timeout if it exceeds previous defaults */
+ if (op->timeout > timeout)
+ timeout = op->timeout;
+ memset(san_cdb, 0, sizeof(san_cdb));
+ san_cdb[0] = SANITIZE_OP;
+ if (op->overwrite)
+ san_cdb[1] = SANITIZE_SA_OVERWRITE;
+ else if (op->block)
+ san_cdb[1] = SANITIZE_SA_BLOCK_ERASE;
+ else if (op->crypto)
+ san_cdb[1] = SANITIZE_SA_CRYPTO_ERASE;
+ else if (op->fail)
+ san_cdb[1] = SANITIZE_SA_EXIT_FAIL_MODE;
+ else
+ return SG_LIB_SYNTAX_ERROR;
+ if (immed)
+ san_cdb[1] |= 0x80;
+ if (op->znr) /* added sbc4r07 */
+ san_cdb[1] |= 0x40;
+ if (op->ause)
+ san_cdb[1] |= 0x20;
+ sg_put_unaligned_be16((uint16_t)param_lst_len, san_cdb + 7);
+
+ if (op->verbose > 1) {
+ char b[128];
+
+ pr2serr(" Sanitize cdb: %s\n",
+ sg_get_command_str(san_cdb, SANITIZE_OP_LEN, false,
+ sizeof(b), b));
+ if (op->verbose > 2) {
+ if (param_lst_len > 0) {
+ pr2serr(" Parameter list contents:\n");
+ hex2stderr((const uint8_t *)param_lstp, param_lst_len, -1);
+ }
+ pr2serr(" Sanitize command timeout: %d seconds\n", timeout);
+ }
+ }
+ if (op->dry_run) {
+ pr2serr("Due to --dry-run option, bypassing SANITIZE command\n");
+ return 0;
+ }
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ pr2serr("Sanitize: out of memory\n");
+ return -1;
+ }
+ set_scsi_pt_cdb(ptvp, san_cdb, sizeof(san_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_out(ptvp, (uint8_t *)param_lstp, param_lst_len);
+ res = do_scsi_pt(ptvp, sg_fd, timeout, op->verbose);
+ ret = sg_cmds_process_resp(ptvp, "Sanitize", res, true /*noisy */,
+ op->verbose, &sense_cat);
+ if (-1 == ret) {
+ if (get_scsi_pt_transport_err(ptvp))
+ ret = SG_LIB_TRANSPORT_ERROR;
+ else
+ ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
+ } else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ case SG_LIB_CAT_MEDIUM_HARD:
+ {
+ bool valid;
+ int slen;
+ uint64_t ull = 0;
+
+ slen = get_scsi_pt_sense_len(ptvp);
+ valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+ if (valid)
+ pr2serr("Medium or hardware error starting at "
+ "lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
+ }
+ ret = sense_cat;
+ break;
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else {
+ ret = 0;
+ if (op->verbose)
+ pr2serr("Sanitize command %s without error\n",
+ (immed ? "launched" : "completed"));
+ }
+
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+#define VPD_DEVICE_ID 0x83
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define TPROTO_ISCSI 5
+
+static char *
+get_lu_name(const uint8_t * bp, int u_len, char * b, int b_len)
+{
+ int len, off, sns_dlen, dlen, k;
+ uint8_t u_sns[512];
+ char * cp;
+
+ len = u_len - 4;
+ bp += 4;
+ off = -1;
+ if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
+ 8 /* SCSI name string (sns) */,
+ 3 /* UTF-8 */)) {
+ sns_dlen = bp[off + 3];
+ memcpy(u_sns, bp + off + 4, sns_dlen);
+ /* now want to check if this is iSCSI */
+ off = -1;
+ if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_TPORT,
+ 8 /* SCSI name string (sns) */,
+ 3 /* UTF-8 */)) {
+ if ((0x80 & bp[1]) && (TPROTO_ISCSI == (bp[0] >> 4))) {
+ snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+ return b;
+ }
+ }
+ } else
+ sns_dlen = 0;
+ if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
+ 3 /* NAA */, 1 /* binary */)) {
+ dlen = bp[off + 3];
+ if (! ((8 == dlen) || (16 ==dlen)))
+ return b;
+ cp = b;
+ for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+ snprintf(cp, b_len, "%02x", bp[off + 4 + k]);
+ cp += 2;
+ b_len -= 2;
+ }
+ } else if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
+ 2 /* EUI */, 1 /* binary */)) {
+ dlen = bp[off + 3];
+ if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen)))
+ return b;
+ cp = b;
+ for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+ snprintf(cp, b_len, "%02x", bp[off + 4 + k]);
+ cp += 2;
+ b_len -= 2;
+ }
+ } else if (sns_dlen > 0)
+ snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+ return b;
+}
+
+#define SAFE_STD_INQ_RESP_LEN 36
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_DEVICE_ID 0x83
+
+static int
+print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen, int verbose)
+{
+ int res, k, n, verb, pdt, has_sn, has_di;
+ uint8_t b[256];
+ char a[256];
+ char pdt_name[64];
+
+ verb = (verbose > 1) ? verbose - 1 : 0;
+ memset(sinq_resp, 0, max_rlen);
+ res = sg_ll_inquiry(fd, false, false /* evpd */, 0 /* pg_op */, b,
+ SAFE_STD_INQ_RESP_LEN, 1, verb);
+ if (res)
+ return res;
+ n = b[4] + 5;
+ if (n > SAFE_STD_INQ_RESP_LEN)
+ n = SAFE_STD_INQ_RESP_LEN;
+ memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen);
+ if (n == SAFE_STD_INQ_RESP_LEN) {
+ pdt = b[0] & PDT_MASK;
+ printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n",
+ (const char *)(b + 8), (const char *)(b + 16),
+ (const char *)(b + 32),
+ sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt);
+ if (verbose)
+ printf(" PROTECT=%d\n", !!(b[5] & 1));
+ if (b[5] & 1)
+ printf(" << supports protection information>>\n");
+ } else {
+ pr2serr("Short INQUIRY response: %d bytes, expect at least 36\n", n);
+ return SG_LIB_CAT_OTHER;
+ }
+ res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_SUPPORTED_VPDS, b,
+ SAFE_STD_INQ_RESP_LEN, 1, verb);
+ if (res) {
+ if (verbose)
+ pr2serr("VPD_SUPPORTED_VPDS gave res=%d\n", res);
+ return 0;
+ }
+ if (VPD_SUPPORTED_VPDS != b[1]) {
+ if (verbose)
+ pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
+ return 0;
+ }
+ n = sg_get_unaligned_be16(b + 2);
+ if (n > (SAFE_STD_INQ_RESP_LEN - 4))
+ n = (SAFE_STD_INQ_RESP_LEN - 4);
+ for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) {
+ if (VPD_UNIT_SERIAL_NUM == b[4 + k])
+ ++has_sn;
+ else if (VPD_DEVICE_ID == b[4 + k]) {
+ ++has_di;
+ break;
+ }
+ }
+ if (has_sn) {
+ res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_UNIT_SERIAL_NUM,
+ b, sizeof(b), 1, verb);
+ if (res) {
+ if (verbose)
+ pr2serr("VPD_UNIT_SERIAL_NUM gave res=%d\n", res);
+ return 0;
+ }
+ if (VPD_UNIT_SERIAL_NUM != b[1]) {
+ if (verbose)
+ pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
+ return 0;
+ }
+ n = sg_get_unaligned_be16(b + 2);
+ if (n > (int)(sizeof(b) - 4))
+ n = (sizeof(b) - 4);
+ printf(" Unit serial number: %.*s\n", n, (const char *)(b + 4));
+ }
+ if (has_di) {
+ res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_DEVICE_ID, b,
+ sizeof(b), 1, verb);
+ if (res) {
+ if (verbose)
+ pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
+ return 0;
+ }
+ if (VPD_DEVICE_ID != b[1]) {
+ if (verbose)
+ pr2serr("VPD_DEVICE_ID corrupted\n");
+ return 0;
+ }
+ n = sg_get_unaligned_be16(b + 2);
+ if (n > (int)(sizeof(b) - 4))
+ n = (sizeof(b) - 4);
+ n = strlen(get_lu_name(b, n + 4, a, sizeof(a)));
+ if (n > 0)
+ printf(" LU name: %.*s\n", n, a);
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool got_stdin = false;
+ int k, res, c, infd, progress, vb, n, resp_len, err;
+ int sg_fd = -1;
+ int param_lst_len = 0;
+ int ret = -1;
+ const char * device_name = NULL;
+ char ebuff[EBUFF_SZ];
+ char b[80];
+ uint8_t rsBuff[DEF_REQS_RESP_LEN];
+ uint8_t * wBuff = NULL;
+ uint8_t * free_wBuff = NULL;
+ struct opts_t opts;
+ struct opts_t * op;
+ struct stat a_stat;
+ uint8_t inq_resp[SAFE_STD_INQ_RESP_LEN];
+
+ op = &opts;
+ memset(op, 0, sizeof(opts));
+ op->count = 1;
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "ABc:CdDeFhi:IOp:Qt:T:vVwzZ",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'A':
+ op->ause = true;
+ break;
+ case 'B':
+ op->block = true;
+ break;
+ case 'c':
+ op->count = sg_get_num(optarg);
+ if ((op->count < 1) || (op->count > 31)) {
+ pr2serr("bad argument to '--count', expect 1 to 31\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'C':
+ op->crypto = true;
+ break;
+ case 'd':
+ op->desc = true;
+ break;
+ case 'D':
+ op->dry_run = true;
+ break;
+ case 'e':
+ op->early = true;
+ break;
+ case 'F':
+ op->fail = true;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'i':
+ op->ipl = sg_get_num(optarg);
+ if ((op->ipl < 1) || (op->ipl > 65535)) {
+ pr2serr("bad argument to '--ipl', expect 1 to 65535\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'I':
+ op->invert = true;
+ break;
+ case 'O':
+ op->overwrite = true;
+ break;
+ case 'p':
+ op->pattern_fn = optarg;
+ break;
+ case 'Q':
+ op->quick = true;
+ break;
+ case 't':
+ op->timeout = sg_get_num(optarg);
+ if (op->timeout < 0) {
+ pr2serr("bad argument to '--timeout=SECS', want 0 or more\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'T':
+ op->test = sg_get_num(optarg);
+ if ((op->test < 0) || (op->test > 3)) {
+ pr2serr("bad argument to '--test', expect 0 to 3\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->verbose;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ case 'w':
+ op->wait = true;
+ break;
+ case 'z':
+ ++op->zero;
+ break;
+ case 'Z':
+ op->znr = true;
+ break;
+ 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;
+ }
+ }
+#ifdef DEBUG
+ pr2serr("In DEBUG mode, ");
+ if (op->verbose_given && op->version_given) {
+ pr2serr("but override: '-vV' given, zero verbose and continue\n");
+ op->verbose_given = false;
+ op->version_given = false;
+ op->verbose = 0;
+ } else if (! op->verbose_given) {
+ pr2serr("set '-vv'\n");
+ op->verbose = 2;
+ } else
+ pr2serr("keep verbose=%d\n", op->verbose);
+#else
+ if (op->verbose_given && op->version_given)
+ pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
+#endif
+ if (op->version_given) {
+ pr2serr(ME "version: %s\n", version_str);
+ return 0;
+ }
+
+ if (NULL == device_name) {
+ pr2serr("Missing device name!\n\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ vb = op->verbose;
+ n = (int)op->block + (int)op->crypto + (int)op->fail + (int)op->overwrite;
+ if (1 != n) {
+ pr2serr("one and only one of '--block', '--crypto', '--fail' or "
+ "'--overwrite' please\n");
+ return SG_LIB_CONTRADICT;
+ }
+ if (op->overwrite) {
+ if (op->zero) {
+ if (op->pattern_fn) {
+ pr2serr("confused: both '--pattern=PF' and '--zero' "
+ "options\n");
+ return SG_LIB_CONTRADICT;
+ }
+ op->ipl = 4;
+ } else {
+ if (NULL == op->pattern_fn) {
+ pr2serr("'--overwrite' requires '--pattern=PF' or '--zero' "
+ "option\n");
+ return SG_LIB_CONTRADICT;
+ }
+ got_stdin = (0 == strcmp(op->pattern_fn, "-"));
+ if (! got_stdin) {
+ memset(&a_stat, 0, sizeof(a_stat));
+ if (stat(op->pattern_fn, &a_stat) < 0) {
+ err = errno;
+ pr2serr("pattern file: unable to stat(%s): %s\n",
+ op->pattern_fn, safe_strerror(err));
+ ret = sg_convert_errno(err);
+ goto err_out;
+ }
+ if (op->ipl <= 0) {
+ op->ipl = (int)a_stat.st_size;
+ if (op->ipl > MAX_XFER_LEN) {
+ pr2serr("pattern file length exceeds 65535 bytes, "
+ "need '--ipl=LEN' option\n");
+ return SG_LIB_FILE_ERROR;
+ }
+ }
+ }
+ if (op->ipl < 1) {
+ pr2serr("'--overwrite' requires '--ipl=LEN' option if can't "
+ "get PF length\n");
+ return SG_LIB_CONTRADICT;
+ }
+ }
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
+ if (sg_fd < 0) {
+ if (op->verbose)
+ pr2serr(ME "open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ ret = sg_convert_errno(-sg_fd);
+ goto err_out;
+ }
+
+ ret = print_dev_id(sg_fd, inq_resp, sizeof(inq_resp), op->verbose);
+ if (ret)
+ goto err_out;
+
+ if (op->overwrite) {
+ param_lst_len = op->ipl + 4;
+ wBuff = (uint8_t*)sg_memalign(op->ipl + 4, 0, &free_wBuff, false);
+ if (NULL == wBuff) {
+ pr2serr("unable to allocate %d bytes of memory with calloc()\n",
+ op->ipl + 4);
+ ret = sg_convert_errno(ENOMEM);
+ goto err_out;
+ }
+ if (op->zero) {
+ if (2 == op->zero) /* treat -zz as fill with 0xff bytes */
+ memset(wBuff + 4, 0xff, op->ipl);
+ else
+ memset(wBuff + 4, 0, op->ipl);
+ } else {
+ if (got_stdin) {
+ infd = STDIN_FILENO;
+ if (sg_set_binary_mode(STDIN_FILENO) < 0)
+ perror("sg_set_binary_mode");
+ } else {
+ if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
+ "reading", op->pattern_fn);
+ perror(ebuff);
+ ret = sg_convert_errno(err);
+ goto err_out;
+ } else if (sg_set_binary_mode(infd) < 0)
+ perror("sg_set_binary_mode");
+ }
+ res = read(infd, wBuff + 4, op->ipl);
+ if (res < 0) {
+ err = errno;
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+ op->pattern_fn);
+ perror(ebuff);
+ if (! got_stdin)
+ close(infd);
+ ret = sg_convert_errno(err);
+ goto err_out;
+ }
+ if (res < op->ipl) {
+ pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+ op->ipl, op->pattern_fn, res);
+ pr2serr(" so pad with 0x0 bytes and continue\n");
+ }
+ if (! got_stdin)
+ close(infd);
+ }
+ wBuff[0] = op->count & 0x1f;
+ if (op->test)
+ wBuff[0] |= ((op->test & 0x3) << 5);
+ if (op->invert)
+ wBuff[0] |= 0x80;
+ sg_put_unaligned_be16((uint16_t)op->ipl, wBuff + 2);
+ }
+
+ if ((! op->quick) && (! op->fail))
+ sg_warn_and_wait("SANITIZE", device_name, true);
+
+ ret = do_sanitize(sg_fd, op, wBuff, param_lst_len);
+ if (ret) {
+ sg_get_category_sense_str(ret, sizeof(b), b, vb);
+ pr2serr("Sanitize failed: %s\n", b);
+ }
+
+ if ((0 == ret) && (! op->early) && (! op->wait)) {
+ for (k = 0; ;++k) { /* unbounded, exits via break */
+ if (op->dry_run && (k > 0)) {
+ pr2serr("Due to --dry-run option, leave poll loop\n");
+ break;
+ }
+ sg_sleep_secs(POLL_DURATION_SECS);
+ memset(rsBuff, 0x0, sizeof(rsBuff));
+ res = sg_ll_request_sense(sg_fd, op->desc, rsBuff, sizeof(rsBuff),
+ 1, vb);
+ if (res) {
+ ret = res;
+ if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr("Request Sense command not supported\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+ pr2serr("bad field in Request Sense cdb\n");
+ if (op->desc) {
+ pr2serr("Descriptor type sense may not be supported, "
+ "try again with fixed type\n");
+ op->desc = false;
+ continue;
+ }
+ } else {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("Request Sense: %s\n", b);
+ if (0 == vb)
+ pr2serr(" try the '-v' option for more "
+ "information\n");
+ }
+ break;
+ }
+ /* "Additional sense length" same in descriptor and fixed */
+ resp_len = rsBuff[7] + 8;
+ if (vb > 2) {
+ pr2serr("Parameter data in hex\n");
+ hex2stderr(rsBuff, resp_len, -1);
+ }
+ progress = -1;
+ sg_get_sense_progress_fld(rsBuff, resp_len, &progress);
+ if (progress < 0) {
+ ret = res;
+ if (vb > 1)
+ pr2serr("No progress indication found, iteration %d\n",
+ k + 1);
+ if ((0 == k) && vb)
+ pr2serr("Sanitize seems to be successful and finished "
+ "quickly\n");
+ /* N.B. exits first time there isn't a progress indication */
+ break;
+ } else
+ printf("Progress indication: %d%% done\n",
+ (progress * 100) / 65536);
+ }
+ }
+
+err_out:
+ if (free_wBuff)
+ free(free_wBuff);
+ if (sg_fd >= 0) {
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-res);
+ }
+ }
+ if (0 == op->verbose) {
+ if (! sg_if_can2stderr("sg_sanitize failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' "
+ "or '-vv' for more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}