aboutsummaryrefslogtreecommitdiff
path: root/src/sg_format.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_format.c')
-rw-r--r--src/sg_format.c1729
1 files changed, 1729 insertions, 0 deletions
diff --git a/src/sg_format.c b/src/sg_format.c
new file mode 100644
index 00000000..4f3793bd
--- /dev/null
+++ b/src/sg_format.c
@@ -0,0 +1,1729 @@
+/*
+ * sg_format : format a SCSI disk
+ * potentially with a different number of blocks and block size
+ *
+ * formerly called blk512-linux.c (v0.4)
+ *
+ * Copyright (C) 2003 Grant Grundler grundler at parisc-linux dot org
+ * Copyright (C) 2003 James Bottomley jejb at parisc-linux dot org
+ * Copyright (C) 2005-2022 Douglas Gilbert dgilbert at interlog dot com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * See https://www.t10.org for relevant standards and drafts. The most recent
+ * draft is SBC-4 revision 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <unistd.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_unaligned.h"
+#include "sg_pr2serr.h"
+#include "sg_pt.h"
+
+static const char * version_str = "1.68 20220609";
+
+
+#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */
+
+#define SHORT_TIMEOUT 20 /* 20 seconds unless --wait given */
+#define FORMAT_TIMEOUT (20 * 3600) /* 20 hours ! */
+#define FOUR_TBYTE (4LL * 1000 * 1000 * 1000 * 1000)
+#define LONG_FORMAT_TIMEOUT (40 * 3600) /* 40 hours */
+#define EIGHT_TBYTE (FOUR_TBYTE * 2)
+#define VLONG_FORMAT_TIMEOUT (80 * 3600) /* 3 days, 8 hours */
+
+#define POLL_DURATION_SECS 60
+#define POLL_DURATION_FFMT_SECS 10
+#define DEF_POLL_TYPE_RS false /* false -> test unit ready;
+ true -> request sense */
+#define MAX_BUFF_SZ 252
+
+/* FORMAT UNIT (SBC) and FORMAT MEDIUM (SSC) share the same opcode */
+#define SG_FORMAT_MEDIUM_CMD 0x4
+#define SG_FORMAT_MEDIUM_CMDLEN 6
+
+/* FORMAT WITH PRESET (new in sbc4r18) */
+#define SG_FORMAT_WITH_PRESET_CMD 0x38
+#define SG_FORMAT_WITH_PRESET_CMDLEN 10
+
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+
+struct opts_t {
+ bool cmplst; /* -C value */
+ bool cmplst_given;
+ bool dry_run; /* -d */
+ bool early; /* -e */
+ bool fmtmaxlba; /* -b (only with F_WITH_PRESET) */
+ bool fwait; /* -w (negated form IMMED) */
+ bool ip_def; /* -I */
+ bool long_lba; /* -l */
+ bool mode6; /* -6 */
+ bool pinfo; /* -p, deprecated, prefer fmtpinfo */
+ bool poll_type; /* -x 0|1 */
+ bool poll_type_given;
+ bool preset; /* -E */
+ bool quick; /* -Q */
+ bool do_rcap16; /* -l */
+ bool resize; /* -r */
+ bool rto_req; /* -R, deprecated, prefer fmtpinfo */
+ bool verbose_given;
+ bool verify; /* -y */
+ bool version_given;
+ int dcrt; /* -D (can be given once or twice) */
+ int lblk_sz; /* -s value */
+ int ffmt; /* -t value; fast_format if > 0 */
+ int fmtpinfo;
+ int format; /* -F */
+ uint32_t p_id; /* set by argument of --preset=id */
+ int mode_page; /* -M value */
+ int pfu; /* -P value */
+ int pie; /* -q value */
+ int sec_init; /* -S */
+ int tape; /* -T <format>, def: -1 */
+ int timeout; /* -m SECS, def: depends on IMMED bit */
+ int verbose; /* -v */
+ int64_t blk_count; /* -c value */
+ int64_t total_byte_count; /* from READ CAPACITY command */
+ const char * device_name;
+};
+
+
+
+static struct option long_options[] = {
+ {"count", required_argument, 0, 'c'},
+ {"cmplst", required_argument, 0, 'C'},
+ {"dcrt", no_argument, 0, 'D'},
+ {"dry-run", no_argument, 0, 'd'},
+ {"dry_run", no_argument, 0, 'd'},
+ {"early", no_argument, 0, 'e'},
+ {"ffmt", required_argument, 0, 't'},
+ {"fmtmaxlba", no_argument, 0, 'b'},
+ {"fmtpinfo", required_argument, 0, 'f'},
+ {"format", no_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"ip-def", no_argument, 0, 'I'},
+ {"ip_def", no_argument, 0, 'I'},
+ {"long", no_argument, 0, 'l'},
+ {"mode", required_argument, 0, 'M'},
+ {"pinfo", no_argument, 0, 'p'},
+ {"pfu", required_argument, 0, 'P'},
+ {"pie", required_argument, 0, 'q'},
+ {"poll", required_argument, 0, 'x'},
+ {"preset", required_argument, 0, 'E'},
+ {"quick", no_argument, 0, 'Q'},
+ {"resize", no_argument, 0, 'r'},
+ {"rto_req", no_argument, 0, 'R'},
+ {"security", no_argument, 0, 'S'},
+ {"six", no_argument, 0, '6'},
+ {"size", required_argument, 0, 's'},
+ {"tape", required_argument, 0, 'T'},
+ {"timeout", required_argument, 0, 'm'},
+ {"verbose", no_argument, 0, 'v'},
+ {"verify", no_argument, 0, 'y'},
+ {"version", no_argument, 0, 'V'},
+ {"wait", no_argument, 0, 'w'},
+ {0, 0, 0, 0},
+};
+
+static const char * fu_s = "Format unit";
+static const char * fm_s = "Format medium";
+static const char * fwp_s = "Format with preset";
+
+
+static void
+usage()
+{
+ printf("Usage:\n"
+ " sg_format [--cmplst=0|1] [--count=COUNT] [--dcrt] "
+ "[--dry-run] [--early]\n"
+ " [--ffmt=FFMT] [--fmtmaxlba] [--fmtpinfo=FPI] "
+ "[--format] [--help]\n"
+ " [--ip-def] [--long] [--mode=MP] [--pfu=PFU] "
+ "[--pie=PIE]\n"
+ " [--pinfo] [--poll=PT] [--preset=ID] [--quick] "
+ "[--resize]\n"
+ " [--rto_req] [--security] [--six] [--size=LB_SZ] "
+ "[--tape=FM]\n"
+ " [--timeout=SECS] [--verbose] [--verify] "
+ "[--version] [--wait]\n"
+ " DEVICE\n"
+ " where:\n"
+ " --cmplst=0|1\n"
+ " -C 0|1 sets CMPLST bit in format cdb "
+ "(def: 1; if FFMT: 0)\n"
+ " --count=COUNT|-c COUNT number of blocks to report "
+ "after format or\n"
+ " resize. Format default is "
+ "same as current\n"
+ " --dcrt|-D disable certification (doesn't "
+ "verify media)\n"
+ " use twice to enable certification and "
+ "set FOV bit\n"
+ " --dry-run|-d bypass device modifying commands (i.e. "
+ "don't format)\n"
+ " --early|-e exit once format started (user can "
+ "monitor progress)\n"
+ " --ffmt=FFMT|-t FFMT fast format (def: 0 -> slow, "
+ "may visit every\n"
+ " block). 1 and 2 are fast formats; "
+ "1: after\n"
+ " format, unwritten data read "
+ "without error\n"
+ " --fmtpinfo=FPI|-f FPI FMTPINFO field value "
+ "(default: 0)\n"
+ " --format|-F do FORMAT UNIT (default: report current "
+ "count and size)\n"
+ " use thrice for FORMAT UNIT command "
+ "only\n"
+ " --fmtmaxlba|-b sets FMTMAXLBA field in FORMAT WITH "
+ "PRESET\n"
+ " --help|-h prints out this usage message\n"
+ " --ip-def|-I use default initialization pattern\n"
+ " --long|-l allow for 64 bit lbas (default: assume "
+ "32 bit lbas)\n"
+ " --mode=MP|-M MP mode page (def: 1 -> RW error "
+ "recovery mpage)\n"
+ " --pie=PIE|-q PIE Protection Information Exponent "
+ "(default: 0)\n"
+ " --pinfo|-p set upper bit of FMTPINFO field\n"
+ " (deprecated, use '--fmtpinfo=FPI' "
+ "instead)\n"
+ " --poll=PT|-x PT PT is poll type, 0 for test unit "
+ "ready\n"
+ " 1 for request sense (def: 0 (1 "
+ "for tape and\n"
+ " format with preset))\n");
+ printf(" --preset=ID|-E ID do FORMAT WITH PRESET command "
+ "with PRESET\n"
+ " IDENTIFIER field set to ID\n"
+ " --quick|-Q start format without pause for user "
+ "intervention\n"
+ " (i.e. no time to reconsider)\n"
+ " --resize|-r resize (rather than format) to COUNT "
+ "value\n"
+ " --rto_req|-R set lower bit of FMTPINFO field\n"
+ " (deprecated use '--fmtpinfo=FPI' "
+ "instead)\n"
+ " --security|-S set security initialization (SI) bit\n"
+ " --six|-6 use 6 byte MODE SENSE/SELECT to probe "
+ "disk\n"
+ " (def: use 10 byte MODE SENSE/SELECT)\n"
+ " --size=LB_SZ|-s LB_SZ bytes per logical block, "
+ "defaults to DEVICE's\n"
+ " current logical block size. Only "
+ "needed to\n"
+ " change current logical block "
+ "size\n"
+ " --tape=FM|-T FM request FORMAT MEDIUM with FORMAT "
+ "field set\n"
+ " to FM (def: 0 --> default format)\n"
+ " --timeout=SECS|-m SECS FORMAT UNIT/MEDIUM command "
+ "timeout in seconds\n"
+ " --verbose|-v increase verbosity\n"
+ " --verify|-y sets VERIFY bit in FORMAT MEDIUM (tape)\n"
+ " --version|-V print version details and exit\n"
+ " --wait|-w format commands wait until format "
+ "operations complete\n"
+ " (default: set IMMED=1 and poll with "
+ "Test Unit Ready)\n\n"
+ "\tExample: sg_format --format /dev/sdc\n\n"
+ "This utility formats a SCSI disk [FORMAT UNIT] or resizes "
+ "it. Alternatively\nif '--tape=FM' is given formats a tape "
+ "[FORMAT MEDIUM]. Another alternative\nis doing the FORMAT "
+ "WITH PRESET command when '--preset=ID' is given.\n\n");
+ printf("WARNING: This utility will destroy all the data on the "
+ "DEVICE when\n\t '--format', '--tape=FM' or '--preset=ID' "
+ "is given. Double check\n\t that you have specified the "
+ "correct DEVICE.\n");
+}
+
+/* Invokes a SCSI FORMAT MEDIUM command (SSC). Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_format_medium(int sg_fd, bool verify, bool immed, int format,
+ void * paramp, int transfer_len, int timeout, bool noisy,
+ int verbose)
+{
+ int ret, res, sense_cat;
+ uint8_t fm_cdb[SG_FORMAT_MEDIUM_CMDLEN] =
+ {SG_FORMAT_MEDIUM_CMD, 0, 0, 0, 0, 0};
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+ struct sg_pt_base * ptvp;
+
+ if (verify)
+ fm_cdb[1] |= 0x2;
+ if (immed)
+ fm_cdb[1] |= 0x1;
+ if (format)
+ fm_cdb[2] |= (0xf & format);
+ if (transfer_len > 0)
+ sg_put_unaligned_be16(transfer_len, fm_cdb + 3);
+ if (verbose) {
+ char b[128];
+
+ pr2serr(" %s cdb: %s\n", fm_s,
+ sg_get_command_str(fm_cdb, SG_FORMAT_MEDIUM_CMDLEN,
+ false, sizeof(b), b));
+ }
+
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ pr2serr("%s: out of memory\n", __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ set_scsi_pt_cdb(ptvp, fm_cdb, sizeof(fm_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, transfer_len);
+ res = do_scsi_pt(ptvp, sg_fd, timeout, verbose);
+ ret = sg_cmds_process_resp(ptvp, fm_s, res, noisy, 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;
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else {
+ ret = 0;
+ if (verbose)
+ pr2serr("%s command %s without error\n", fm_s,
+ (immed ? "launched" : "completed"));
+ }
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+/* Invokes a SCSI FORMAT WITH PRESET command (SBC). Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_format_with_preset(int sg_fd, bool immed, bool fmtmaxlba,
+ uint32_t preset_id, int timeout, bool noisy,
+ int verbose)
+{
+ int ret, res, sense_cat;
+ uint8_t fwp_cdb[SG_FORMAT_WITH_PRESET_CMDLEN] =
+ {SG_FORMAT_WITH_PRESET_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+ struct sg_pt_base * ptvp;
+
+ if (immed)
+ fwp_cdb[1] |= 0x80;
+ if (fmtmaxlba)
+ fwp_cdb[1] |= 0x40;
+ if (preset_id > 0)
+ sg_put_unaligned_be32(preset_id, fwp_cdb + 2);
+ if (verbose) {
+ char b[128];
+
+ pr2serr(" %s cdb: %s\n", fwp_s,
+ sg_get_command_str(fwp_cdb,
+ SG_FORMAT_WITH_PRESET_CMDLEN,
+ false, sizeof(b), b));
+ }
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ pr2serr("%s: out of memory\n", __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ set_scsi_pt_cdb(ptvp, fwp_cdb, sizeof(fwp_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ res = do_scsi_pt(ptvp, sg_fd, timeout, verbose);
+ ret = sg_cmds_process_resp(ptvp, fwp_s, res, noisy, 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;
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else {
+ ret = 0;
+ if (verbose)
+ pr2serr("%s command %s without error\n", fwp_s,
+ (immed ? "launched" : "completed"));
+ }
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+/* Return 0 on success, else see sg_ll_format_unit_v2() */
+static int
+scsi_format_unit(int fd, const struct opts_t * op)
+{
+ bool need_param_lst, longlist, ip_desc, first;
+ bool immed = ! op->fwait;
+ int res, progress, pr, rem, param_sz, off, resp_len, tmout;
+ int poll_wait_secs;
+ int vb = op->verbose;
+ const int SH_FORMAT_HEADER_SZ = 4;
+ const int LONG_FORMAT_HEADER_SZ = 8;
+ const int INIT_PATTERN_DESC_SZ = 4;
+ const int max_param_sz = LONG_FORMAT_HEADER_SZ + INIT_PATTERN_DESC_SZ;
+ uint8_t * param;
+ uint8_t * free_param = NULL;
+ char b[80];
+
+ param = sg_memalign(max_param_sz, 0, &free_param, false);
+ if (NULL == param) {
+ pr2serr("%s: unable to obtain heap for parameter list\n",
+ __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ if (immed)
+ tmout = SHORT_TIMEOUT;
+ else {
+ if (op->total_byte_count > EIGHT_TBYTE)
+ tmout = VLONG_FORMAT_TIMEOUT;
+ else if (op->total_byte_count > FOUR_TBYTE)
+ tmout = LONG_FORMAT_TIMEOUT;
+ else
+ tmout = FORMAT_TIMEOUT;
+ }
+ if (op->timeout > tmout)
+ tmout = op->timeout;
+ longlist = (op->pie > 0); /* only set LONGLIST if PI_EXPONENT>0 */
+ ip_desc = (op->ip_def || op->sec_init);
+ off = longlist ? LONG_FORMAT_HEADER_SZ : SH_FORMAT_HEADER_SZ;
+ param[0] = op->pfu & 0x7; /* PROTECTION_FIELD_USAGE (bits 2-0) */
+ param[1] = (immed ? 0x2 : 0); /* FOV=0, [DPRY,DCRT,STPF,IP=0] */
+ if (1 == op->dcrt)
+ param[1] |= 0xa0; /* FOV=1, DCRT=1 */
+ else if (op->dcrt > 1)
+ param[1] |= 0x80; /* FOV=1, DCRT=0 */
+ if (ip_desc) {
+ param[1] |= 0x88; /* FOV=1, IP=1 */
+ if (op->sec_init)
+ param[off + 0] = 0x20; /* SI=1 in IP desc */
+ }
+ if (longlist)
+ param[3] = (op->pie & 0xf);/* PROTECTION_INTERVAL_EXPONENT */
+ /* with the long parameter list header, P_I_INFORMATION is always 0 */
+
+ need_param_lst = (immed || op->cmplst || (op->dcrt > 0) || ip_desc ||
+ (op->pfu > 0) || (op->pie > 0));
+ param_sz = need_param_lst ?
+ (off + (ip_desc ? INIT_PATTERN_DESC_SZ : 0)) : 0;
+
+ if (op->dry_run) {
+ res = 0;
+ pr2serr("Due to --dry-run option bypassing FORMAT UNIT "
+ "command\n");
+ if (vb) {
+ if (need_param_lst) {
+ pr2serr(" %s would have received parameter "
+ "list: ", fu_s);
+ hex2stderr(param, max_param_sz, -1);
+ } else
+ pr2serr(" %s would not have received a "
+ "parameter list\n", fu_s);
+ pr2serr(" %s cdb fields: fmtpinfo=0x%x, "
+ "longlist=%d, fmtdata=%d, cmplst=%d, "
+ "ffmt=%d [timeout=%d secs]\n", fu_s,
+ op->fmtpinfo, longlist, need_param_lst,
+ op->cmplst, op->ffmt, tmout);
+ }
+ } else
+ res = sg_ll_format_unit_v2(fd, op->fmtpinfo, longlist,
+ need_param_lst, op->cmplst, 0,
+ op->ffmt, tmout, param, param_sz,
+ true, vb);
+ if (free_param)
+ free(free_param);
+
+ if (res) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("%s command: %s\n", fu_s, b);
+ return res;
+ } else if (op->verbose)
+ pr2serr("%s command %s without error\n", fu_s,
+ (immed ? "launched" : "completed"));
+ if (! immed)
+ return 0;
+
+ if (! op->dry_run)
+ printf("\n%s has started\n", fu_s);
+
+ if (op->early) {
+ if (immed)
+ printf("%s continuing,\n request sense or "
+ "test unit ready can be used to monitor "
+ "progress\n", fu_s);
+ return 0;
+ }
+
+ if (op->dry_run) {
+ printf("No point in polling for progress, so exit\n");
+ return 0;
+ }
+ poll_wait_secs = op->ffmt ? POLL_DURATION_FFMT_SECS :
+ POLL_DURATION_SECS;
+ if (! op->poll_type) {
+ for(first = true; ; first = false) {
+ sg_sleep_secs(poll_wait_secs);
+ progress = -1;
+ res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
+ true, (vb > 1) ? (vb - 1) : 0);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fu_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fu_s);
+ break;
+ }
+ }
+ }
+ if (op->poll_type || (SG_LIB_CAT_NOT_READY == res)) {
+ uint8_t * reqSense;
+ uint8_t * free_reqSense = NULL;
+
+ reqSense = sg_memalign(MAX_BUFF_SZ, 0, &free_reqSense, false);
+ if (NULL == reqSense) {
+ pr2serr("%s: unable to obtain heap for Request "
+ "Sense\n", __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ for(first = true; ; first = false) {
+ sg_sleep_secs(poll_wait_secs);
+ memset(reqSense, 0x0, MAX_BUFF_SZ);
+ res = sg_ll_request_sense(fd, false, reqSense,
+ MAX_BUFF_SZ, false,
+ (vb > 1) ? (vb - 1) : 0);
+ if (res) {
+ pr2serr("polling with Request Sense command "
+ "failed [res=%d]\n", res);
+ break;
+ }
+ resp_len = reqSense[7] + 8;
+ if (vb > 1) {
+ pr2serr("Parameter data in hex:\n");
+ hex2stderr(reqSense, resp_len, 1);
+ }
+ progress = -1;
+ sg_get_sense_progress_fld(reqSense, resp_len,
+ &progress);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fu_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fu_s);
+ break;
+ }
+ }
+ if (free_reqSense)
+ free(free_reqSense);
+ }
+ printf("FORMAT UNIT Complete\n");
+ return 0;
+}
+
+/* Return 0 on success, else see sg_ll_format_medium() above */
+static int
+scsi_format_medium(int fd, const struct opts_t * op)
+{
+ bool first;
+ bool immed = ! op->fwait;
+ int res, progress, pr, rem, resp_len, tmout;
+ int vb = op->verbose;
+ char b[80];
+
+ if (immed)
+ tmout = SHORT_TIMEOUT;
+ else {
+ if (op->total_byte_count > EIGHT_TBYTE)
+ tmout = VLONG_FORMAT_TIMEOUT;
+ else if (op->total_byte_count > FOUR_TBYTE)
+ tmout = LONG_FORMAT_TIMEOUT;
+ else
+ tmout = FORMAT_TIMEOUT;
+ }
+ if (op->timeout > tmout)
+ tmout = op->timeout;
+ if (op->dry_run) {
+ res = 0;
+ pr2serr("Due to --dry-run option bypassing %s command\n",
+ fm_s);
+ } else
+ res = sg_ll_format_medium(fd, op->verify, immed,
+ 0xf & op->tape, NULL, 0, tmout,
+ true, vb);
+ if (res) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("%s command: %s\n", fm_s, b);
+ return res;
+ }
+ if (! immed)
+ return 0;
+
+ if (! op->dry_run)
+ printf("\n%s has started\n", fm_s);
+ if (op->early) {
+ if (immed)
+ printf("%s continuing,\n request sense or "
+ "test unit ready can be used to monitor "
+ "progress\n", fm_s);
+ return 0;
+ }
+
+ if (op->dry_run) {
+ printf("No point in polling for progress, so exit\n");
+ return 0;
+ }
+ if (! op->poll_type) {
+ for(first = true; ; first = false) {
+ sg_sleep_secs(POLL_DURATION_SECS);
+ progress = -1;
+ res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
+ true, (vb > 1) ? (vb - 1) : 0);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fm_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fm_s);
+ break;
+ }
+ }
+ }
+ if (op->poll_type || (SG_LIB_CAT_NOT_READY == res)) {
+ uint8_t * reqSense;
+ uint8_t * free_reqSense = NULL;
+
+ reqSense = sg_memalign(MAX_BUFF_SZ, 0, &free_reqSense, false);
+ if (NULL == reqSense) {
+ pr2serr("%s: unable to obtain heap for Request "
+ "Sense\n", __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ for(first = true; ; first = false) {
+ sg_sleep_secs(POLL_DURATION_SECS);
+ memset(reqSense, 0x0, MAX_BUFF_SZ);
+ res = sg_ll_request_sense(fd, false, reqSense,
+ MAX_BUFF_SZ, false,
+ (vb > 1) ? (vb - 1) : 0);
+ if (res) {
+ pr2serr("polling with Request Sense command "
+ "failed [res=%d]\n", res);
+ break;
+ }
+ resp_len = reqSense[7] + 8;
+ if (vb > 1) {
+ pr2serr("Parameter data in hex:\n");
+ hex2stderr(reqSense, resp_len, 1);
+ }
+ progress = -1;
+ sg_get_sense_progress_fld(reqSense, resp_len,
+ &progress);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fm_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fm_s);
+ break;
+ }
+ }
+ if (free_reqSense)
+ free(free_reqSense);
+ }
+ printf("FORMAT MEDIUM Complete\n");
+ return 0;
+}
+
+/* Return 0 on success, else see sg_ll_format_medium() above */
+static int
+scsi_format_with_preset(int fd, const struct opts_t * op)
+{
+ bool first;
+ bool immed = ! op->fwait;
+ int res, progress, pr, rem, resp_len, tmout;
+ int vb = op->verbose;
+ char b[80];
+
+ if (immed)
+ tmout = SHORT_TIMEOUT;
+ else {
+ if (op->total_byte_count > EIGHT_TBYTE)
+ tmout = VLONG_FORMAT_TIMEOUT;
+ else if (op->total_byte_count > FOUR_TBYTE)
+ tmout = LONG_FORMAT_TIMEOUT;
+ else
+ tmout = FORMAT_TIMEOUT;
+ }
+ if (op->timeout > tmout)
+ tmout = op->timeout;
+ if (op->dry_run) {
+ res = 0;
+ pr2serr("Due to --dry-run option bypassing FORMAT WITH "
+ "PRESET command\n");
+ } else
+ res = sg_ll_format_with_preset(fd, immed, op->fmtmaxlba,
+ op->p_id, tmout, true, vb);
+ if (res) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("%s command: %s\n", fwp_s, b);
+ return res;
+ }
+ if (! immed)
+ return 0;
+
+ if (! op->dry_run)
+ printf("\n%s has started\n", fwp_s);
+ if (op->early) {
+ if (immed)
+ printf("%s continuing,\n Request sense can "
+ "be used to monitor progress\n", fwp_s);
+ return 0;
+ }
+
+ if (op->dry_run) {
+ printf("No point in polling for progress, so exit\n");
+ return 0;
+ }
+ if (! op->poll_type) {
+ for(first = true; ; first = false) {
+ sg_sleep_secs(POLL_DURATION_SECS);
+ progress = -1;
+ res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
+ true, (vb > 1) ? (vb - 1) : 0);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fwp_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fwp_s);
+ break;
+ }
+ }
+ }
+ if (op->poll_type || (SG_LIB_CAT_NOT_READY == res)) {
+ uint8_t * reqSense;
+ uint8_t * free_reqSense = NULL;
+
+ reqSense = sg_memalign(MAX_BUFF_SZ, 0, &free_reqSense, false);
+ if (NULL == reqSense) {
+ pr2serr("%s: unable to obtain heap for Request "
+ "Sense\n", __func__);
+ return sg_convert_errno(ENOMEM);
+ }
+ for(first = true; ; first = false) {
+ sg_sleep_secs(POLL_DURATION_SECS);
+ memset(reqSense, 0x0, MAX_BUFF_SZ);
+ res = sg_ll_request_sense(fd, false, reqSense,
+ MAX_BUFF_SZ, false,
+ (vb > 1) ? (vb - 1) : 0);
+ if (res) {
+ pr2serr("polling with Request Sense command "
+ "failed [res=%d]\n", res);
+ break;
+ }
+ resp_len = reqSense[7] + 8;
+ if (vb > 1) {
+ pr2serr("Parameter data in hex:\n");
+ hex2stderr(reqSense, resp_len, 1);
+ }
+ progress = -1;
+ sg_get_sense_progress_fld(reqSense, resp_len,
+ &progress);
+ if (progress >= 0) {
+ pr = (progress * 100) / 65536;
+ rem = ((progress * 100) % 65536) / 656;
+ printf("%s in progress, %d.%02d%% done\n",
+ fwp_s, pr, rem);
+ } else {
+ if (first && op->verbose)
+ pr2serr("%s seems to be successful "
+ "and finished quickly\n",
+ fwp_s);
+ break;
+ }
+ }
+ if (free_reqSense)
+ free(free_reqSense);
+ }
+ printf("FORMAT WITH PRESET Complete\n");
+ return 0;
+}
+
+#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
+#define MAX_VPD_RESP_LEN 256
+
+static int
+print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen,
+ const struct opts_t * op)
+{
+ int k, n, verb, pdt, has_sn, has_di;
+ int res = 0;
+ uint8_t * b;
+ uint8_t * free_b = NULL;
+ char a[MAX_VPD_RESP_LEN];
+ char pdt_name[64];
+
+ verb = (op->verbose > 1) ? op->verbose - 1 : 0;
+ memset(sinq_resp, 0, max_rlen);
+ b = sg_memalign(MAX_VPD_RESP_LEN, 0, &free_b, false);
+ if (NULL == b) {
+ res = sg_convert_errno(ENOMEM);
+ goto out;
+ }
+ /* Standard INQUIRY */
+ res = sg_ll_inquiry(fd, false, false, 0, b, SAFE_STD_INQ_RESP_LEN,
+ true, verb);
+ if (res)
+ goto out;
+ 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 (op->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);
+ res = SG_LIB_CAT_OTHER;
+ goto out;
+ }
+ res = sg_ll_inquiry(fd, false, true, VPD_SUPPORTED_VPDS, b,
+ SAFE_STD_INQ_RESP_LEN, true, verb);
+ if (res) {
+ if (op->verbose)
+ pr2serr("VPD_SUPPORTED_VPDS gave res=%d\n", res);
+ res = 0;
+ goto out;
+ }
+ if (VPD_SUPPORTED_VPDS != b[1]) {
+ if (op->verbose)
+ pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
+ goto out;
+ }
+ 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, MAX_VPD_RESP_LEN,
+ true, verb);
+ if (res) {
+ if (op->verbose)
+ pr2serr("VPD_UNIT_SERIAL_NUM gave res=%d\n",
+ res);
+ res = 0;
+ goto out;
+ }
+ if (VPD_UNIT_SERIAL_NUM != b[1]) {
+ if (op->verbose)
+ pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
+ goto out;
+ }
+ n = sg_get_unaligned_be16(b + 2);
+ if (n > (int)(MAX_VPD_RESP_LEN - 4))
+ n = (MAX_VPD_RESP_LEN - 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, MAX_VPD_RESP_LEN, true, verb);
+ if (res) {
+ if (op->verbose)
+ pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
+ res = 0;
+ goto out;
+ }
+ if (VPD_DEVICE_ID != b[1]) {
+ if (op->verbose)
+ pr2serr("VPD_DEVICE_ID corrupted\n");
+ goto out;
+ }
+ n = sg_get_unaligned_be16(b + 2);
+ if (n > (int)(MAX_VPD_RESP_LEN - 4))
+ n = (MAX_VPD_RESP_LEN - 4);
+ n = strlen(get_lu_name(b, n + 4, a, sizeof(a)));
+ if (n > 0)
+ printf(" LU name: %.*s\n", n, a);
+ }
+out:
+ if (free_b)
+ free(free_b);
+ return res;
+}
+
+#define RCAP_REPLY_LEN 32
+
+/* Returns block size or -2 if do_16==0 and the number of blocks is too
+ * big, or returns -1 for other error. */
+static int
+print_read_cap(int fd, struct opts_t * op)
+{
+ int res = 0;
+ uint8_t * resp_buff;
+ uint8_t * free_resp_buff = NULL;
+ unsigned int last_blk_addr, block_size;
+ uint64_t llast_blk_addr;
+ int64_t ll;
+ char b[80];
+
+ resp_buff = sg_memalign(RCAP_REPLY_LEN, 0, &free_resp_buff, false);
+ if (NULL == resp_buff) {
+ pr2serr("%s: unable to obtain heap\n", __func__);
+ res = -1;
+ goto out;
+ }
+ if (op->do_rcap16) {
+ res = sg_ll_readcap_16(fd, false /* pmi */, 0 /* llba */,
+ resp_buff, RCAP_REPLY_LEN, true,
+ op->verbose);
+ if (0 == res) {
+ llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0);
+ block_size = sg_get_unaligned_be32(resp_buff + 8);
+ printf("Read Capacity (16) results:\n");
+ printf(" Protection: prot_en=%d, p_type=%d, "
+ "p_i_exponent=%d\n",
+ !!(resp_buff[12] & 0x1),
+ ((resp_buff[12] >> 1) & 0x7),
+ ((resp_buff[13] >> 4) & 0xf));
+ printf(" Logical block provisioning: lbpme=%d, "
+ "lbprz=%d\n", !!(resp_buff[14] & 0x80),
+ !!(resp_buff[14] & 0x40));
+ printf(" Logical blocks per physical block "
+ "exponent=%d\n", resp_buff[13] & 0xf);
+ printf(" Lowest aligned logical block address=%d\n",
+ 0x3fff & sg_get_unaligned_be16(resp_buff +
+ 14));
+ printf(" Number of logical blocks=%" PRIu64 "\n",
+ llast_blk_addr + 1);
+ printf(" Logical block size=%u bytes\n",
+ block_size);
+ ll = (int64_t)(llast_blk_addr + 1) * block_size;
+ if (ll > op->total_byte_count)
+ op->total_byte_count = ll;
+ res = (int)block_size;
+ goto out;
+ }
+ } else {
+ res = sg_ll_readcap_10(fd, false /* pmi */, 0 /* lba */,
+ resp_buff, 8, true, op->verbose);
+ if (0 == res) {
+ last_blk_addr = sg_get_unaligned_be32(resp_buff + 0);
+ block_size = sg_get_unaligned_be32(resp_buff + 4);
+ if (0xffffffff == last_blk_addr) {
+ if (op->verbose)
+ printf("Read Capacity (10) response "
+ "indicates that Read Capacity "
+ "(16) is required\n");
+ res = -2;
+ goto out;
+ }
+ printf("Read Capacity (10) results:\n");
+ printf(" Number of logical blocks=%u\n",
+ last_blk_addr + 1);
+ printf(" Logical block size=%u bytes\n",
+ block_size);
+ ll = (int64_t)(last_blk_addr + 1) * block_size;
+ if (ll > op->total_byte_count)
+ op->total_byte_count = ll;
+ res = (int)block_size;
+ goto out;
+ }
+ }
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("READ CAPACITY (%d): %s\n", (op->do_rcap16 ? 16 : 10), b);
+ res = -1;
+out:
+ if (free_resp_buff)
+ free(free_resp_buff);
+ return res;
+}
+
+/* Use MODE SENSE(6 or 10) to fetch blocks descriptor(s), if any. Analyze
+ * the first block descriptor and if required, start preparing for a
+ * MODE SELECT(6 or 10). Returns 0 on success. */
+static int
+fetch_block_desc(int fd, uint8_t * dbuff, int * calc_lenp, int * bd_lb_szp,
+ struct opts_t * op)
+{
+ bool first = true;
+ bool prob;
+ int bd_lbsz, bd_len, dev_specific_param, offset, res, rq_lb_sz;
+ int rsp_len;
+ int resid = 0;
+ int vb = op->verbose;
+ uint64_t ull;
+ int64_t ll;
+ char b[80];
+
+again_with_long_lba:
+ memset(dbuff, 0, MAX_BUFF_SZ);
+ if (op->mode6)
+ res = sg_ll_mode_sense6(fd, false /* DBD */, 0 /* current */,
+ op->mode_page, 0 /* subpage */, dbuff,
+ MAX_BUFF_SZ, true, vb);
+ else
+ res = sg_ll_mode_sense10_v2(fd, op->long_lba, false /* DBD */,
+ 0 /* current */, op->mode_page,
+ 0 /* subpage */, dbuff,
+ MAX_BUFF_SZ, 0, &resid, true,
+ vb);
+ if (res) {
+ if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+ if (op->long_lba && (! op->mode6))
+ pr2serr("bad field in MODE SENSE (%d) "
+ "[longlba flag not supported?]\n",
+ (op->mode6 ? 6 : 10));
+ else
+ pr2serr("bad field in MODE SENSE (%d) "
+ "[mode_page %d not supported?]\n",
+ (op->mode6 ? 6 : 10), op->mode_page);
+ } else {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("MODE SENSE (%d) command: %s\n",
+ (op->mode6 ? 6 : 10), b);
+ }
+ if (0 == vb)
+ pr2serr(" try '-v' for more information\n");
+ return res;
+ }
+ rsp_len = (resid > 0) ? (MAX_BUFF_SZ - resid) : MAX_BUFF_SZ;
+ if (rsp_len < 0) {
+ pr2serr("%s: resid=%d implies negative response "
+ "length of %d\n", __func__, resid, rsp_len);
+ return SG_LIB_WILD_RESID;
+ }
+ *calc_lenp = sg_msense_calc_length(dbuff, rsp_len, op->mode6, &bd_len);
+ if (op->mode6) {
+ if (rsp_len < 4) {
+ pr2serr("%s: MS(6) response length too short (%d)\n",
+ __func__, rsp_len);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ dev_specific_param = dbuff[2];
+ op->long_lba = false;
+ offset = 4;
+ /* prepare for mode select */
+ dbuff[0] = 0;
+ dbuff[1] = 0;
+ dbuff[2] = 0;
+ } else { /* MODE SENSE(10) */
+ if (rsp_len < 8) {
+ pr2serr("%s: MS(10) response length too short (%d)\n",
+ __func__, rsp_len);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ dev_specific_param = dbuff[3];
+ op->long_lba = !! (dbuff[4] & 1);
+ offset = 8;
+ /* prepare for mode select */
+ dbuff[0] = 0;
+ dbuff[1] = 0;
+ dbuff[2] = 0;
+ dbuff[3] = 0;
+ }
+ if (rsp_len < *calc_lenp) {
+ pr2serr("%s: MS response length truncated (%d < %d)\n",
+ __func__, rsp_len, *calc_lenp);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if ((offset + bd_len) < *calc_lenp)
+ dbuff[offset + bd_len] &= 0x7f; /* clear PS bit in mpage */
+ prob = false;
+ bd_lbsz = 0;
+ *bd_lb_szp = bd_lbsz;
+ rq_lb_sz = op->lblk_sz;
+ if (first) {
+ first = false;
+ printf("Mode Sense (block descriptor) data, prior to "
+ "changes:\n");
+ }
+ if (dev_specific_param & 0x40)
+ printf(" <<< Write Protect (WP) bit set >>>\n");
+ if (bd_len > 0) {
+ ull = op->long_lba ? sg_get_unaligned_be64(dbuff + offset) :
+ sg_get_unaligned_be32(dbuff + offset);
+ bd_lbsz = op->long_lba ?
+ sg_get_unaligned_be32(dbuff + offset + 12) :
+ sg_get_unaligned_be24(dbuff + offset + 5);
+ *bd_lb_szp = bd_lbsz;
+ if (! op->long_lba) {
+ if (0xffffffff == ull) {
+ if (vb)
+ pr2serr("block count maxed out, set "
+ "<<longlba>>\n");
+ op->long_lba = true;
+ op->mode6 = false;
+ op->do_rcap16 = true;
+ goto again_with_long_lba;
+ } else if ((rq_lb_sz > 0) && (rq_lb_sz < bd_lbsz) &&
+ (((ull * bd_lbsz) / rq_lb_sz) >=
+ 0xffffffff)) {
+ if (vb)
+ pr2serr("number of blocks will max "
+ "out, set <<longlba>>\n");
+ op->long_lba = true;
+ op->mode6 = false;
+ op->do_rcap16 = true;
+ goto again_with_long_lba;
+ }
+ }
+ if (op->long_lba) {
+ printf(" <<< longlba flag set (64 bit lba) >>>\n");
+ if (bd_len != 16)
+ prob = true;
+ } else if (bd_len != 8)
+ prob = true;
+ printf(" Number of blocks=%" PRIu64 " [0x%" PRIx64 "]\n",
+ ull, ull);
+ printf(" Block size=%d [0x%x]\n", bd_lbsz, bd_lbsz);
+ ll = (int64_t)ull * bd_lbsz;
+ if (ll > op->total_byte_count)
+ op->total_byte_count = ll;
+ } else {
+ printf(" No block descriptors present\n");
+ prob = true;
+ }
+ if (op->resize || (op->format && ((op->blk_count != 0) ||
+ ((rq_lb_sz > 0) && (rq_lb_sz != bd_lbsz))))) {
+ /* want to run MODE SELECT, prepare now */
+
+ if (prob) {
+ pr2serr("Need to perform MODE SELECT (to change "
+ "number or blocks or block length)\n");
+ pr2serr("but (single) block descriptor not found "
+ "in earlier MODE SENSE\n");
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (op->blk_count != 0) { /* user supplied blk count */
+ if (op->long_lba)
+ sg_put_unaligned_be64(op->blk_count,
+ dbuff + offset);
+ else
+ sg_put_unaligned_be32(op->blk_count,
+ dbuff + offset);
+ } else if ((rq_lb_sz > 0) && (rq_lb_sz != bd_lbsz))
+ /* 0 implies max capacity with new LB size */
+ memset(dbuff + offset, 0, op->long_lba ? 8 : 4);
+
+ if ((rq_lb_sz > 0) && (rq_lb_sz != bd_lbsz)) {
+ if (op->long_lba)
+ sg_put_unaligned_be32((uint32_t)rq_lb_sz,
+ dbuff + offset + 12);
+ else
+ sg_put_unaligned_be24((uint32_t)rq_lb_sz,
+ dbuff + offset + 5);
+ }
+ }
+ return 0;
+}
+
+static int
+parse_cmd_line(struct opts_t * op, int argc, char **argv)
+{
+ int j;
+ int64_t ll;
+
+ op->cmplst = true; /* will be set false if FFMT > 0 */
+ op->mode_page = RW_ERROR_RECOVERY_PAGE;
+ op->poll_type = DEF_POLL_TYPE_RS;
+ op->tape = -1;
+ while (1) {
+ int option_index = 0;
+ int c;
+
+ c = getopt_long(argc, argv,
+ "bc:C:dDeE:f:FhIlm:M:pP:q:QrRs:St:T:vVwx:y6",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'b':
+ op->fmtmaxlba = true;
+ break;
+ case 'c':
+ if (0 == strcmp("-1", optarg))
+ op->blk_count = -1;
+ else {
+ op->blk_count = sg_get_llnum(optarg);
+ if (-1 == op->blk_count) {
+ pr2serr("bad argument to '--count'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ break;
+ case 'C':
+ j = sg_get_num(optarg);
+ if ((j < 0) || (j > 1)) {
+ pr2serr("bad argument to '--cmplst', want 0 "
+ "or 1\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->cmplst_given = true;
+ op->cmplst = !! j;
+ break;
+ case 'd':
+ op->dry_run = true;
+ break;
+ case 'D':
+ ++op->dcrt;
+ break;
+ case 'e':
+ op->early = true;
+ break;
+ case 'E':
+ ll = sg_get_llnum(optarg);
+ if ((ll < 0) || (ll > UINT32_MAX)) {
+ pr2serr("bad argument to '--preset', need 32 "
+ "bit integer\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->p_id = (uint32_t)ll;
+ op->preset = true;
+ op->poll_type = 1; /* poll with REQUEST SENSE */
+ break;
+ case 'f':
+ op->fmtpinfo = sg_get_num(optarg);
+ if ((op->fmtpinfo < 0) || ( op->fmtpinfo > 3)) {
+ pr2serr("bad argument to '--fmtpinfo', "
+ "accepts 0 to 3 inclusive\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'F':
+ ++op->format;
+ break;
+ case 'h':
+ usage();
+ return SG_LIB_OK_FALSE;
+ case 'I':
+ op->ip_def = true;
+ break;
+ case 'l':
+ op->long_lba = true;
+ op->do_rcap16 = true;
+ break;
+ case 'm':
+ op->timeout = sg_get_num(optarg);
+ if (op->timeout < 0) {
+ pr2serr("bad argument to '--timeout=', "
+ "accepts 0 or more\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'M':
+ op->mode_page = sg_get_num(optarg);
+ if ((op->mode_page < 0) || ( op->mode_page > 62)) {
+ pr2serr("bad argument to '--mode', accepts "
+ "0 to 62 inclusive\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'p':
+ op->pinfo = true;
+ break;
+ case 'P':
+ op->pfu = sg_get_num(optarg);
+ if ((op->pfu < 0) || ( op->pfu > 7)) {
+ pr2serr("bad argument to '--pfu', accepts 0 "
+ "to 7 inclusive\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'q':
+ op->pie = sg_get_num(optarg);
+ if ((op->pie < 0) || (op->pie > 15)) {
+ pr2serr("bad argument to '--pie', accepts 0 "
+ "to 15 inclusive\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'Q':
+ op->quick = true;
+ break;
+ case 'r':
+ op->resize = true;
+ break;
+ case 'R':
+ op->rto_req = true;
+ break;
+ case 's':
+ op->lblk_sz = sg_get_num(optarg);
+ if (op->lblk_sz <= 0) {
+ pr2serr("bad argument to '--size', want arg "
+ "> 0\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'S':
+ op->sec_init = true;
+ break;
+ case 't':
+ op->ffmt = sg_get_num(optarg);
+ if ((op->ffmt < 0) || ( op->ffmt > 3)) {
+ pr2serr("bad argument to '--ffmt', "
+ "accepts 0 to 3 inclusive\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'T':
+ if (('-' == optarg[0]) && ('1' == optarg[1]) &&
+ ('\0' == optarg[2])) {
+ op->tape = -1;
+ break;
+ }
+ op->tape = sg_get_num(optarg);
+ if ((op->tape < 0) || ( op->tape > 15)) {
+ pr2serr("bad argument to '--tape', accepts "
+ "0 to 15 inclusive\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->fwait = true;
+ break;
+ case 'x': /* false: TUR; true: request sense */
+ op->poll_type = !! sg_get_num(optarg);
+ op->poll_type_given = true;
+ break;
+ case 'y':
+ op->verify = true;
+ break;
+ case '6':
+ op->mode6 = true;
+ break;
+ default:
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == op->device_name) {
+ op->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("sg_format version: %s\n", version_str);
+ return SG_LIB_OK_FALSE;
+ }
+ if (NULL == op->device_name) {
+ pr2serr("no DEVICE name given\n\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (((int)(op->format > 0) + (int)(op->tape >= 0) + (int)op->preset)
+ > 1) {
+ pr2serr("Can choose only one of: '--format', '--tape=' and "
+ "'--preset='\n");
+ return SG_LIB_CONTRADICT;
+ }
+ if (op->ip_def && op->sec_init) {
+ pr2serr("'--ip_def' and '--security' contradict, choose "
+ "one\n");
+ return SG_LIB_CONTRADICT;
+ }
+ if (op->resize) {
+ if (op->format) {
+ pr2serr("both '--format' and '--resize' not "
+ "permitted\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ } else if (0 == op->blk_count) {
+ pr2serr("'--resize' needs a '--count' (other than "
+ "0)\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ } else if (0 != op->lblk_sz) {
+ pr2serr("'--resize' not compatible with '--size'\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ }
+ }
+ if ((op->pinfo > 0) || (op->rto_req > 0) || (op->fmtpinfo > 0)) {
+ if ((op->pinfo || op->rto_req) && op->fmtpinfo) {
+ pr2serr("confusing with both '--pinfo' or "
+ "'--rto_req' together with\n'--fmtpinfo', "
+ "best use '--fmtpinfo' only\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ }
+ if (op->pinfo)
+ op->fmtpinfo |= 2;
+ if (op->rto_req)
+ op->fmtpinfo |= 1;
+ }
+ if ((op->ffmt > 0) && (! op->cmplst_given))
+ op->cmplst = false; /* SBC-4 silent; FFMT&&CMPLST unlikely */
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int bd_lb_sz, calc_len, pdt, res, rq_lb_sz, vb;
+ int fd = -1;
+ int ret = 0;
+ const int dbuff_sz = MAX_BUFF_SZ;
+ const int inq_resp_sz = SAFE_STD_INQ_RESP_LEN;
+ struct opts_t * op;
+ uint8_t * dbuff;
+ uint8_t * free_dbuff = NULL;
+ uint8_t * inq_resp;
+ uint8_t * free_inq_resp = NULL;
+ struct opts_t opts;
+ char b[80];
+
+ op = &opts;
+ memset(op, 0, sizeof(opts));
+ ret = parse_cmd_line(op, argc, argv);
+ if (ret)
+ return (SG_LIB_OK_FALSE == ret) ? 0 : ret;
+ vb = op->verbose;
+
+ dbuff = sg_memalign(dbuff_sz, 0, &free_dbuff, false);
+ inq_resp = sg_memalign(inq_resp_sz, 0, &free_inq_resp, false);
+ if ((NULL == dbuff) || (NULL == inq_resp)) {
+ pr2serr("Unable to allocate heap\n");
+ ret = sg_convert_errno(ENOMEM);
+ goto out;
+ }
+
+ if ((fd = sg_cmds_open_device(op->device_name, false, vb)) < 0) {
+ pr2serr("error opening device file: %s: %s\n",
+ op->device_name, safe_strerror(-fd));
+ ret = sg_convert_errno(-fd);
+ goto out;
+ }
+
+ if (op->format > 2)
+ goto format_only;
+
+ ret = print_dev_id(fd, inq_resp, inq_resp_sz, op);
+ if (ret) {
+ if (op->dry_run) {
+ pr2serr("INQUIRY failed, assume device is a disk\n");
+ pdt = 0;
+ } else
+ goto out;
+ } else
+ pdt = PDT_MASK & inq_resp[0];
+ if (op->format) {
+ if ((PDT_DISK != pdt) && (PDT_OPTICAL != pdt) &&
+ (PDT_RBC != pdt) && (PDT_ZBC != pdt)) {
+ pr2serr("This format is only defined for disks "
+ "(using SBC-2+, ZBC or RBC) and MO media\n");
+ ret = SG_LIB_CAT_MALFORMED;
+ goto out;
+ }
+ } else if (op->tape >= 0) {
+ if (! ((PDT_TAPE == pdt) || (PDT_MCHANGER == pdt) ||
+ (PDT_ADC == pdt))) {
+ pr2serr("This format is only defined for tapes\n");
+ ret = SG_LIB_CAT_MALFORMED;
+ goto out;
+ }
+ goto format_med;
+ } else if (op->preset)
+ goto format_with_pre;
+
+ ret = fetch_block_desc(fd, dbuff, &calc_len, &bd_lb_sz, op);
+ if (ret) {
+ if (op->dry_run) {
+ /* pick some numbers ... */
+ calc_len = 1024 * 1024 * 1024;
+ bd_lb_sz = 512;
+ } else
+ goto out;
+ }
+ rq_lb_sz = op->lblk_sz;
+ if (op->resize || (op->format && ((op->blk_count != 0) ||
+ ((rq_lb_sz > 0) && (rq_lb_sz != bd_lb_sz))))) {
+ /* want to run MODE SELECT */
+ if (op->dry_run) {
+ pr2serr("Due to --dry-run option bypass MODE "
+ "SELECT(%d) command\n", (op->mode6 ? 6 : 10));
+ res = 0;
+ } else {
+ bool sp = true; /* may not be able to save pages */
+
+again_sp_false:
+ if (op->mode6)
+ res = sg_ll_mode_select6(fd, true /* PF */,
+ sp, dbuff, calc_len,
+ true, vb);
+ else
+ res = sg_ll_mode_select10(fd, true /* PF */,
+ sp, dbuff, calc_len,
+ true, vb);
+ if ((SG_LIB_CAT_ILLEGAL_REQ == res) && sp) {
+ pr2serr("Try MODE SELECT again with SP=0 "
+ "this time\n");
+ sp = false;
+ goto again_sp_false;
+ }
+ }
+ ret = res;
+ if (res) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("MODE SELECT command: %s\n", b);
+ if (0 == vb)
+ pr2serr(" try '-v' for more information\n");
+ goto out;
+ }
+ }
+ if (op->resize) {
+ printf("Resize operation seems to have been successful\n");
+ goto out;
+ } else if (! op->format) {
+ res = print_read_cap(fd, op);
+ if (-2 == res) {
+ op->do_rcap16 = true;
+ res = print_read_cap(fd, op);
+ }
+ if (res < 0)
+ ret = -1;
+ if ((res > 0) && (bd_lb_sz > 0) &&
+ (res != (int)bd_lb_sz)) {
+ printf(" Warning: mode sense and read capacity "
+ "report different block sizes [%d,%d]\n",
+ bd_lb_sz, res);
+ printf(" Probably needs format\n");
+ }
+ if ((PDT_TAPE == pdt) || (PDT_MCHANGER == pdt) ||
+ (PDT_ADC == pdt))
+ printf("No changes made. To format use '--tape='.\n");
+ else
+ printf("No changes made. To format use '--format'. "
+ "To resize use '--resize'\n");
+ goto out;
+ }
+
+ if (op->format) {
+format_only:
+ if (! op->quick)
+ sg_warn_and_wait("FORMAT UNIT", op->device_name, true);
+ res = scsi_format_unit(fd, op);
+ ret = res;
+ if (res) {
+ pr2serr("FORMAT UNIT failed\n");
+ if (0 == vb)
+ pr2serr(" try '-v' for more "
+ "information\n");
+ }
+ }
+ goto out;
+
+format_med:
+ if (! op->poll_type_given) /* SSC-5 specifies REQUEST SENSE polling */
+ op->poll_type = true;
+ if (! op->quick)
+ sg_warn_and_wait("FORMAT MEDIUM", op->device_name, true);
+ res = scsi_format_medium(fd, op);
+ ret = res;
+ if (res) {
+ pr2serr("FORMAT MEDIUM failed\n");
+ if (0 == vb)
+ pr2serr(" try '-v' for more information\n");
+ }
+ goto out;
+
+format_with_pre:
+ if (! op->quick)
+ sg_warn_and_wait("FORMAT WITH PRESET", op->device_name, true);
+ res = scsi_format_with_preset(fd, op);
+ ret = res;
+ if (res) {
+ pr2serr("FORMAT WITH PRESET failed\n");
+ if (0 == vb)
+ pr2serr(" try '-v' for more information\n");
+ }
+
+out:
+ if (free_dbuff)
+ free(free_dbuff);
+ if (free_inq_resp)
+ free(free_inq_resp);
+ if (fd >= 0) {
+ res = sg_cmds_close_device(fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-res);
+ }
+ }
+ if (0 == vb) {
+ if (! sg_if_can2stderr("sg_format failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' "
+ "or '-vv' for more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}