diff options
Diffstat (limited to 'src/sg_format.c')
-rw-r--r-- | src/sg_format.c | 1729 |
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; +} |