aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2018-08-31 19:55:49 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2018-08-31 19:55:49 +0000
commit38a3773e8ef84bd18fcf266df4e697e431c73882 (patch)
tree63a58aa4d6d1152413be290fca89e10b84cf1566
parent175f9fdb1a0851f958dc57488c5ecacd2f6811f4 (diff)
downloadsg3_utils-38a3773e8ef84bd18fcf266df4e697e431c73882.tar.gz
sg_format: FFMT tweaks: default CMPLST to false, shorten poll
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@788 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog10
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg_format.871
-rw-r--r--lib/sg_lib.c8
-rw-r--r--lib/sg_lib_data.c6
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_compare_and_write.c2
-rw-r--r--src/sg_format.c726
-rw-r--r--src/sg_inq.c22
-rw-r--r--src/sg_vpd.c41
-rw-r--r--src/sg_xcopy.c2
11 files changed, 502 insertions, 390 deletions
diff --git a/ChangeLog b/ChangeLog
index 25cd7d7f..cb7093b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for sg3_utils-1.43 [20180815] [svn: r787]
+Changelog for sg3_utils-1.43 [20180831] [svn: r788]
- sg_write_x: where x can be normal, atomic, or(write),
same, scattered, or stream writes with 16 or 32 byte
cdbs (sbc4r04 for atomic, sbc4r11 for scattered)
@@ -19,9 +19,11 @@ Changelog for sg3_utils-1.43 [20180815] [svn: r787]
> 4 TB and 80 hours if > 8 TB
- when changing block size allow for Mode Select
rejecting SP=1 (Save Page): repeat with SP=0
+ - FFMT tweaks: default CMPLST to false, shorten poll
+ - make all data-in and data-out buffers page aligned
- sg_decode sense: add --cdb and --err=ES options
- sg_ses: handle 2 bit EIIOE field in aes dpage
- - increase join array size from 260 to 520 elements
+ - increase join array size from 260 to 520 elements
- add --quiet option to suppress messages
- expand join handling of SAS connectors and others
- expand join debug code
@@ -72,11 +74,9 @@ Changelog for sg3_utils-1.43 [20180815] [svn: r787]
- add --long which decodes more of the NVMe Identify
command responses
- sg_inq+sg_vpd: update Extended inquiry data vpd
- page (spc5r09)
+ page (spc5r09 and 17-142r5)
- block limits and block limit extension VPD pages:
add extra info about corner cases
- - add enclosure services device characteristics
- VPD page (T10/170-142r1 --> ses4r02 ??)
- add maximum inquiry|mode_page change logs fields
to extended inquiry vpd page (spc5r17)
- both now return EDOM (adjusted sg error code) when
diff --git a/debian/changelog b/debian/changelog
index f9052c81..4a553fe7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.43-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Wed, 15 Aug 2018 10:00:00 -0400
+ -- Douglas Gilbert <dgilbert@interlog.com> Fri, 31 Aug 2018 21:00:00 +0200
sg3-utils (1.42-0.1) unstable; urgency=low
diff --git a/doc/sg_format.8 b/doc/sg_format.8
index 11f38a0f..f25a2283 100644
--- a/doc/sg_format.8
+++ b/doc/sg_format.8
@@ -67,27 +67,27 @@ option name.
.TP
\fB\-C\fR, \fB\-\-cmplst\fR={0|1}
sets the CMPLST ("complete list") bit in the FORMAT UNIT cdb to 0 or 1.
-The default is 1 in which case the existing GLIST (grown list) is ignored.
-If the value is 0 then the existing GLIST is taken into account. See the
-LISTS section below. In most cases this bit should be left set; some MO
-disk drives need this bit cleared.
+If the value is 0 then the existing GLIST (grown list) is taken into account.
+If the value is 1 then the existing GLIST is ignored. CMPLST defaults to 1
+apart from when the \fI\-\-ffmt=FFMT\fR option's value is non\-zero in which
+case CMPLST defaults to 0. See the LISTS section below. In most cases this
+bit should be left at its default value.
.TP
\fB\-c\fR, \fB\-\-count\fR=\fICOUNT\fR
where \fICOUNT\fR is the number of blocks to be formatted or media to be
resized to. Can be used with either \fI\-\-format\fR or \fI\-\-resize\fR.
With \fI\-\-format\fR this option need not be given in which case it is
-assumed to be zero. With \fI\-\-format\fR the interpretation of \fICOUNT\fR
-is:
+assumed to be zero.
.br
- (\fICOUNT\fR > 0) : only format the first \fICOUNT\fR blocks and READ
+With \fI\-\-format\fR the interpretation of \fICOUNT\fR is:
.br
- CAPACITY will report \fICOUNT\fR blocks after format
+ (\fICOUNT\fR > 0) : only format the first \fICOUNT\fR blocks and READ
+CAPACITY will report \fICOUNT\fR blocks after format
.br
(\fICOUNT\fR = 0) and block size unchanged : use existing block count
.br
(\fICOUNT\fR = 0) and block size changed : recommended maximum block
-.br
- count for new block size
+count for new block size
.br
(\fICOUNT\fR = \-1) : use recommended maximum block count
.br
@@ -97,14 +97,12 @@ With \fI\-\-resize\fR this option must be given and \fICOUNT\fR has this
interpretation:
.br
(\fICOUNT\fR > 0) : after resize READ CAPACITY will report \fICOUNT\fR
-.br
- blocks
+blocks
.br
(\fICOUNT\fR = 0) : after resize READ CAPACITY will report 0 blocks
.br
(\fICOUNT\fR = \-1) : after resize READ CAPACITY will report its
-.br
- maximum number of blocks
+maximum number of blocks
.br
(\fICOUNT\fR < \-1) : illegal
.br
@@ -128,12 +126,12 @@ would do that is also bypassed when the dry run option is given.
.TP
\fB\-e\fR, \fB\-\-early\fR
during a format operation, The default action of this utility is to poll the
-disk every 60 seconds to determine the progress of the format operation until
-it is finished. When this option is given this utility will exit "early",
-that is as soon as the format operation has commenced. Then the user can
-monitor the progress of the ongoing format operation with other
-utilities (e.g. sg_turs(8) or sg_requests(8)). This option and \fI\-\-wait\fR
-are mutually exclusive.
+disk every 60 seconds (or every 10 seconds if \fIFFMT\fR is non\-zero) to
+determine the progress of the format operation until it is finished. When this
+option is given this utility will exit "early", that is as soon as the format
+operation has commenced. Then the user can monitor the progress of the ongoing
+format operation with other utilities (e.g. sg_turs(8) or sg_requests(8)).
+This option and \fI\-\-wait\fR are mutually exclusive.
.TP
\fB\-t\fR, \fB\-\-ffmt\fR=\fIFFMT\fR
\fIFFMT\fR (fast format) is placed in a field of the same name in the FORMAT
@@ -420,14 +418,15 @@ When the \fI\-\-format\fR option is given without the \fI\-\-wait\fR option
then the SCSI FORMAT UNIT command is issued with the IMMED bit set which
causes the SCSI command to return after it has started the format operation.
The \fI\-\-early\fR option will cause sg_format to exit at that point.
-Otherwise the \fIDEVICE\fR is polled every 60 seconds with TEST UNIT READY
-or REQUEST SENSE commands until it reports an "all clear" (i.e. the format
-operation has completed). Normally these polling commands will result in a
-progress indicator (expressed as a percentage) being output to the screen.
-If the user gets bored watching the progress report then sg_format process
-can be terminated (e.g. with control\-C) without affecting the format
-operation which continues. However a target or device reset (or a power
-cycle) will probably cause the device to become "format corrupt".
+Otherwise the \fIDEVICE\fR is polled every 60 seconds or every 10 seconds
+if \fIFFMT\fR is non\-zero. The poll is with TEST UNIT READY or REQUEST SENSE
+commands until one reports an "all clear" (i.e. the format operation has
+completed). Normally these polling commands will result in a progress
+indicator (expressed as a percentage) being output to the screen. If the user
+gets bored watching the progress report then sg_format process can be
+terminated (e.g. with control\-C) without affecting the format operation
+which continues. However a target or device reset (or a power cycle) will
+probably cause the device to become "format corrupt".
.PP
When the \fI\-\-format\fR (or \fI\-\-tape\fR) and \fI\-\-wait\fR options are
both given then this utility may take a long time to return. In this case
@@ -464,6 +463,11 @@ the manufacturer's maximum recommended value. To see exactly which SCSI
commands are being executed and parameters passed add the "\-vvv" option to
the sg_format command line.
.PP
+The FMTDATA field shown in the FORMAT UNIT cdb does not have a corresponding
+option in this utility. When set in the cdb it indicates an additional
+parameter list will be sent to the \fIDEVICE\fR along with the cdb. It is set
+as required, basically when any field in the parameter list header is set.
+.PP
Short stroking is a technique to trade off capacity for performance on
hard disks. "Hard" disk is often used to mean a storage device with
spinning platters which contain the user data. Solid State Disk (SSD) is
@@ -604,6 +608,17 @@ To format with type 3 protection:
.PP
For the disk shown above this will probably fail because the Extended inquiry
VPD page showed only types 1 and 2 protection are supported.
+.PP
+Here are examples of using fast format (FFMT field in FORMAT UNIT cdb) to
+quickly switch between 512 and 4096 byte logical block size. Assume disk
+starts with 4096 byte logical block size and all important data has been
+backed up.
+.PP
+ # sg_format \-\-format \-\-ffmt=1 \-\-size=512 \-\-count=\-1 /dev/sdd
+.PP
+Now /dev/sdd should have 512 byte logical block size. And to switch it back:
+.PP
+ # sg_format \-\-format \-\-ffmt=1 \-\-size=4096 \-\-count=\-1 /dev/sdd
.SH EXIT STATUS
The exit status of sg_format is 0 when it is successful. Otherwise see
the sg3_utils(8) man page. Unless the \fI\-\-wait\fR option is given, the
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 092de1a1..3e67fda3 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -1997,11 +1997,11 @@ sg_convert_errno(int os_err_num)
static const char * const bad_sense_cat = "Bad sense category";
-/* Yield string associated with sense category. Returns 'buff' (or pointer
- * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
- * yield "Sense category: <sense_cat)val>" string. The original 'sense
+/* Yield string associated with sense category. Returns 'b' (or pointer
+ * to "Bad sense category" if 'b' is NULL). If sense_cat unknown then
+ * yield "Sense category: <sense_cat_val>" string. The original 'sense
* category' concept has been expanded to most detected errors and is
- * returned by these utilties as their exit status value (an (unsigned)
+ * returned by these utilities as their exit status value (an (unsigned)
* 8 bit value where 0 means good (i.e. no errors)). Uses sg_exit2str()
* function. */
const char *
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index efc81b47..5055f357 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -17,7 +17,7 @@
#include "sg_lib_data.h"
-const char * sg_lib_version_str = "2.55 20180814";/* spc5r19, sbc4r15 */
+const char * sg_lib_version_str = "2.56 20180831";/* spc5r19, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
@@ -1670,8 +1670,8 @@ struct sg_lib_value_name_t sg_lib_nvme_cmd_status_arr[] =
{0x11e,18, "Boot partition write prohibited"},
{0x11f, 5, "Invalid controller identifier"},
{0x120, 5, "Invalid secondary controller state"},
- {0x121, 5, "Invalid number of controller resorces"},
- {0x122, 5, "Invalid resorce identifier"},
+ {0x121, 5, "Invalid number of controller resources"},
+ {0x122, 5, "Invalid resource identifier"},
/* Command specific status values, Status Code Type (SCT): 1h
* for NVM (I/O) Command Set */
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 9afa438b..00fd21e1 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -84,7 +84,7 @@ fi
%{_libdir}/*.la
%changelog
-* Wed Aug 15 2018 - dgilbert at interlog dot com
+* Fri Aug 31 2018 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.43
diff --git a/src/sg_compare_and_write.c b/src/sg_compare_and_write.c
index c1a74abc..159e8774 100644
--- a/src/sg_compare_and_write.c
+++ b/src/sg_compare_and_write.c
@@ -166,7 +166,7 @@ usage()
"\n"
"Performs a SCSI COMPARE AND WRITE operation. Sends a double "
"size\nbuffer, the first half is used to compare what is at "
- "LBA for NUM\nblocks. If and only if the comparsion is "
+ "LBA for NUM\nblocks. If and only if the comparison is "
"equal, then the second\nhalf of the buffer is written to "
"LBA for NUM blocks.\n");
}
diff --git a/src/sg_format.c b/src/sg_format.c
index ab19a8a8..af1a033d 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -38,7 +38,7 @@
#include "sg_pr2serr.h"
#include "sg_pt.h"
-static const char * version_str = "1.53 20180809";
+static const char * version_str = "1.55 20180830";
#define RW_ERROR_RECOVERY_PAGE 1 /* can give alternate with --mode=MP */
@@ -51,8 +51,10 @@ static const char * version_str = "1.53 20180809";
#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
#if defined(MSC_VER) || defined(__MINGW32__)
#define HAVE_MS_SLEEP
@@ -71,7 +73,8 @@ static const char * version_str = "1.53 20180809";
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
struct opts_t {
- bool cmplst; /* -C value */
+ bool cmplst; /* -C value */
+ bool cmplst_given;
bool dcrt; /* -D */
bool dry_run; /* -d */
bool early; /* -e */
@@ -89,8 +92,8 @@ struct opts_t {
bool verbose_given;
bool verify; /* -y */
bool version_given;
- int blk_size; /* -s value */
- int ffmt; /* -t value */
+ int lblk_sz; /* -s value */
+ int ffmt; /* -t value; fast_format if > 0 */
int fmtpinfo;
int format; /* -F */
int mode_page; /* -M value */
@@ -105,8 +108,6 @@ struct opts_t {
const char * device_name;
};
-#define MAX_BUFF_SZ 252
-static uint8_t dbuff[MAX_BUFF_SZ];
static struct option long_options[] = {
@@ -162,7 +163,7 @@ usage()
" where:\n"
" --cmplst=0|1\n"
" -C 0|1 sets CMPLST bit in format cdb "
- "(default: 1)\n"
+ "(def: 1; if FFMT: 0)\n"
" --count=COUNT|-c COUNT number of blocks to report "
"after format or\n"
" resize. Format default is "
@@ -173,9 +174,12 @@ usage()
"don't format)\n"
" --early|-e exit once format started (user can "
"monitor progress)\n"
- " --ffmt=FFMT|-t FFMT fast format (def: 0 -> "
- "possibly overwrite\n"
- " whole medium)\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 "
@@ -299,69 +303,83 @@ scsi_format_unit(int fd, const struct opts_t * op)
{
bool need_hdr, longlist, ip_desc;
bool immed = ! op->fwait;
- int res, progress, pr, rem, verb, fmt_pl_sz, off, resp_len, timeout;
+ 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 LO_FORMAT_HEADER_SZ = 8;
+ const int LONG_FORMAT_HEADER_SZ = 8;
const int INIT_PATTERN_DESC_SZ = 4;
- uint8_t fmt_pl[LO_FORMAT_HEADER_SZ + INIT_PATTERN_DESC_SZ];
- uint8_t reqSense[MAX_BUFF_SZ];
+ const int max_param_sz = LONG_FORMAT_HEADER_SZ + INIT_PATTERN_DESC_SZ;
+ uint8_t * param;
+ uint8_t * free_param = NULL;
char b[80];
- memset(fmt_pl, 0, sizeof(fmt_pl));
+ 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)
- timeout = SHORT_TIMEOUT;
+ tmout = SHORT_TIMEOUT;
else {
if (op->total_byte_count > EIGHT_TBYTE)
- timeout = VLONG_FORMAT_TIMEOUT;
+ tmout = VLONG_FORMAT_TIMEOUT;
else if (op->total_byte_count > FOUR_TBYTE)
- timeout = LONG_FORMAT_TIMEOUT;
+ tmout = LONG_FORMAT_TIMEOUT;
else
- timeout = FORMAT_TIMEOUT;
+ tmout = FORMAT_TIMEOUT;
}
- if (op->timeout > timeout)
- timeout = op->timeout;
- longlist = (op->pie > 0);
+ 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 ? LO_FORMAT_HEADER_SZ : SH_FORMAT_HEADER_SZ;
- fmt_pl[0] = op->pfu & 0x7; /* PROTECTION_FIELD_USAGE (bits 2-0) */
- fmt_pl[1] = (immed ? 0x2 : 0); /* FOV=0, [DPRY,DCRT,STPF,IP=0] */
+ 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 (op->dcrt)
- fmt_pl[1] |= 0xa0; /* FOV=1, DCRT=1 */
+ param[1] |= 0xa0; /* FOV=1, DCRT=1 */
if (ip_desc) {
- fmt_pl[1] |= 0x88; /* FOV=1, IP=1 */
+ param[1] |= 0x88; /* FOV=1, IP=1 */
if (op->sec_init)
- fmt_pl[off + 0] = 0x20; /* SI=1 in IP desc */
+ param[off + 0] = 0x20; /* SI=1 in IP desc */
}
if (longlist)
- fmt_pl[3] = (op->pie & 0xf);/* PROTECTION_INTERVAL_EXPONENT */
+ param[3] = (op->pie & 0xf);/* PROTECTION_INTERVAL_EXPONENT */
/* with the long parameter list header, P_I_INFORMATION is always 0 */
need_hdr = (immed || op->cmplst || op->dcrt || ip_desc ||
(op->pfu > 0) || (op->pie > 0));
- fmt_pl_sz = 0;
- if (need_hdr)
- fmt_pl_sz = off + (ip_desc ? INIT_PATTERN_DESC_SZ : 0);
+ param_sz = need_hdr ?
+ (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 (op->verbose) {
- pr2serr("FU would have received: fmt_pl: ");
- hex2stderr(fmt_pl, sizeof(fmt_pl), -1);
- pr2serr(" fmtpinfo=0x%x, longlist=%d, need_hdr=%d, "
- "cmplst=%d, ffmt=%d, timeout=%d\n",
+ if (vb) {
+ if (need_hdr) {
+ pr2serr(" FU would have received parameter "
+ "list: ");
+ hex2stderr(param, max_param_sz, -1);
+ } else
+ pr2serr(" FU would not have received a "
+ "parameter list\n");
+ pr2serr(" FU cdb fields: fmtpinfo=0x%x, "
+ "longlist=%d, fmtdata=%d, cmplst=%d, "
+ "ffmt=%d [timeout=%d secs]\n",
op->fmtpinfo, longlist, need_hdr, op->cmplst,
- op->ffmt, timeout);
+ op->ffmt, tmout);
}
} else
res = sg_ll_format_unit_v2(fd, op->fmtpinfo, longlist,
- need_hdr/* FMTDATA*/, op->cmplst,
- 0 /* DEFECT_LIST_FORMAT */, op->ffmt,
- timeout, fmt_pl, fmt_pl_sz, true,
- op->verbose);
+ need_hdr, 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, op->verbose);
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
pr2serr("Format unit command: %s\n", b);
return res;
}
@@ -383,13 +401,14 @@ scsi_format_unit(int fd, const struct opts_t * op)
printf("No point in polling for progress, so exit\n");
return 0;
}
- verb = (op->verbose > 1) ? (op->verbose - 1) : 0;
+ poll_wait_secs = op->ffmt ? POLL_DURATION_FFMT_SECS :
+ POLL_DURATION_SECS;
if (! op->poll_type) {
for(;;) {
- sleep_for(POLL_DURATION_SECS);
+ sleep_for(poll_wait_secs);
progress = -1;
res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
- true, verb);
+ true, (vb > 1) ? (vb - 1) : 0);
if (progress >= 0) {
pr = (progress * 100) / 65536;
rem = ((progress * 100) % 65536) / 656;
@@ -400,19 +419,28 @@ scsi_format_unit(int fd, const struct opts_t * op)
}
}
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(;;) {
- sleep_for(POLL_DURATION_SECS);
- memset(reqSense, 0x0, sizeof(reqSense));
- res = sg_ll_request_sense(fd, false /* desc */,
- reqSense, sizeof(reqSense),
- false, verb);
+ sleep_for(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 (verb) {
+ if (vb > 1) {
pr2serr("Parameter data in hex:\n");
hex2stderr(reqSense, resp_len, 1);
}
@@ -427,44 +455,9 @@ scsi_format_unit(int fd, const struct opts_t * op)
} else
break;
}
+ if (free_reqSense)
+ free(free_reqSense);
}
-#if 0
- for (k = 0; k < num_rs; ++k) {
- if (k > 0)
- sleep_for(30);
- memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
- res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff,
- maxlen, true, op->verbose);
- if (res) {
- ret = res;
- sg_get_category_sense_str(res, sizeof(b), b,
- op->verbose);
- pr2serr("Request Sense command: %s\n", b);
- break;
- }
- /* "Additional sense length" same in descriptor and fixed */
- resp_len = requestSenseBuff[7] + 8;
- if (op->verbose > 1) {
- pr2serr("Parameter data in hex\n");
- hex2stderr(requestSenseBuff, resp_len, 1);
- }
- progress = -1;
- sg_get_sense_progress_fld(requestSenseBuff, resp_len,
- &progress);
- if (progress < 0) {
- ret = res;
- if (op->verbose > 1)
- pr2serr("No progress indication found, "
- "iteration %d\n", k + 1);
- /* N.B. exits first time there isn't a
- * progress indication */
- break;
- } else
- printf("Progress indication: %d.%02d%% done\n",
- (progress * 100) / 65536,
- ((progress * 100) % 65536) / 656);
- }
-#endif
printf("FORMAT UNIT Complete\n");
return 0;
}
@@ -473,33 +466,33 @@ scsi_format_unit(int fd, const struct opts_t * op)
static int
scsi_format_medium(int fd, const struct opts_t * op)
{
- int res, progress, pr, rem, verb, resp_len, timeout;
+ int res, progress, pr, rem, resp_len, tmout;
+ int vb = op->verbose;
bool immed = ! op->fwait;
- uint8_t reqSense[MAX_BUFF_SZ];
char b[80];
if (immed)
- timeout = SHORT_TIMEOUT;
+ tmout = SHORT_TIMEOUT;
else {
if (op->total_byte_count > EIGHT_TBYTE)
- timeout = VLONG_FORMAT_TIMEOUT;
+ tmout = VLONG_FORMAT_TIMEOUT;
else if (op->total_byte_count > FOUR_TBYTE)
- timeout = LONG_FORMAT_TIMEOUT;
+ tmout = LONG_FORMAT_TIMEOUT;
else
- timeout = FORMAT_TIMEOUT;
+ tmout = FORMAT_TIMEOUT;
}
- if (op->timeout > timeout)
- timeout = op->timeout;
+ if (op->timeout > tmout)
+ tmout = op->timeout;
if (op->dry_run) {
res = 0;
pr2serr("Due to --dry-run option bypassing FORMAT UNIT "
"command\n");
} else
res = sg_ll_format_medium(fd, op->verify, immed,
- 0xf & op->tape, NULL, 0, timeout,
- true, op->verbose);
+ 0xf & op->tape, NULL, 0, tmout,
+ true, vb);
if (res) {
- sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
pr2serr("Format medium command: %s\n", b);
return res;
}
@@ -520,13 +513,12 @@ scsi_format_medium(int fd, const struct opts_t * op)
printf("No point in polling for progress, so exit\n");
return 0;
}
- verb = (op->verbose > 1) ? (op->verbose - 1) : 0;
if (! op->poll_type) {
for(;;) {
sleep_for(POLL_DURATION_SECS);
progress = -1;
res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
- true, verb);
+ true, (vb > 1) ? (vb - 1) : 0);
if (progress >= 0) {
pr = (progress * 100) / 65536;
rem = ((progress * 100) % 65536) / 656;
@@ -537,19 +529,28 @@ scsi_format_medium(int fd, const struct opts_t * op)
}
}
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(;;) {
sleep_for(POLL_DURATION_SECS);
- memset(reqSense, 0x0, sizeof(reqSense));
- res = sg_ll_request_sense(fd, false /* desc */,
- reqSense, sizeof(reqSense),
- false, verb);
+ 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 (verb) {
+ if (vb > 1) {
pr2serr("Parameter data in hex:\n");
hex2stderr(reqSense, resp_len, 1);
}
@@ -564,6 +565,8 @@ scsi_format_medium(int fd, const struct opts_t * op)
} else
break;
}
+ if (free_reqSense)
+ free(free_reqSense);
}
printf("FORMAT MEDIUM Complete\n");
return 0;
@@ -633,23 +636,31 @@ get_lu_name(const uint8_t * bp, int u_len, char * b, int b_len)
#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 res, k, n, verb, pdt, has_sn, has_di;
- uint8_t b[256];
- char a[256];
+ 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);
- res = sg_ll_inquiry(fd, false /* cmddt */, false /* evpd */,
- 0 /* pg_op */, b, SAFE_STD_INQ_RESP_LEN, true,
- verb);
+ 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)
- return res;
+ goto out;
n = b[4] + 5;
if (n > SAFE_STD_INQ_RESP_LEN)
n = SAFE_STD_INQ_RESP_LEN;
@@ -668,19 +679,21 @@ print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen,
} else {
pr2serr("Short INQUIRY response: %d bytes, expect at least "
"36\n", n);
- return SG_LIB_CAT_OTHER;
+ 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);
- return 0;
+ res = 0;
+ goto out;
}
if (VPD_SUPPORTED_VPDS != b[1]) {
if (op->verbose)
pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
- return 0;
+ goto out;
}
n = sg_get_unaligned_be16(b + 2);
if (n > (SAFE_STD_INQ_RESP_LEN - 4))
@@ -691,7 +704,7 @@ print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen,
if (op->verbose)
pr2serr("VPD_SUPPORTED_VPDS "
"dis-ordered\n");
- return 0;
+ goto out;
}
++has_sn;
} else if (VPD_DEVICE_ID == b[4 + k]) {
@@ -701,46 +714,51 @@ print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen,
}
if (has_sn) {
res = sg_ll_inquiry(fd, false, true /* evpd */,
- VPD_UNIT_SERIAL_NUM, b, sizeof(b), true,
- verb);
+ 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);
- return 0;
+ res = 0;
+ goto out;
}
if (VPD_UNIT_SERIAL_NUM != b[1]) {
if (op->verbose)
pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
- return 0;
+ goto out;
}
n = sg_get_unaligned_be16(b + 2);
- if (n > (int)(sizeof(b) - 4))
- n = (sizeof(b) - 4);
+ 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, sizeof(b), true, verb);
+ b, MAX_VPD_RESP_LEN, true, verb);
if (res) {
if (op->verbose)
pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
- return 0;
+ res = 0;
+ goto out;
}
if (VPD_DEVICE_ID != b[1]) {
if (op->verbose)
pr2serr("VPD_DEVICE_ID corrupted\n");
- return 0;
+ goto out;
}
n = sg_get_unaligned_be16(b + 2);
- if (n > (int)(sizeof(b) - 4))
- n = (sizeof(b) - 4);
+ 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);
}
- return 0;
+out:
+ if (free_b)
+ free(free_b);
+ return res;
}
#define RCAP_REPLY_LEN 32
@@ -750,16 +768,24 @@ print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen,
static int
print_read_cap(int fd, struct opts_t * op)
{
- int res;
- uint8_t resp_buff[RCAP_REPLY_LEN];
+ 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, 32, true, op->verbose);
+ 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);
@@ -784,7 +810,8 @@ print_read_cap(int fd, struct opts_t * op)
ll = (int64_t)(llast_blk_addr + 1) * block_size;
if (ll > op->total_byte_count)
op->total_byte_count = ll;
- return (int)block_size;
+ res = (int)block_size;
+ goto out;
}
} else {
res = sg_ll_readcap_10(fd, false /* pmi */, 0 /* lba */,
@@ -797,7 +824,8 @@ print_read_cap(int fd, struct opts_t * op)
printf("Read Capacity (10) response "
"indicates that Read Capacity "
"(16) is required\n");
- return -2;
+ res = -2;
+ goto out;
}
printf("Read Capacity (10) results:\n");
printf(" Number of logical blocks=%u\n",
@@ -807,33 +835,204 @@ print_read_cap(int fd, struct opts_t * op)
ll = (int64_t)(last_blk_addr + 1) * block_size;
if (ll > op->total_byte_count)
op->total_byte_count = ll;
- return (int)block_size;
+ 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);
- return -1;
+ res = -1;
+out:
+ if (free_resp_buff)
+ free(free_resp_buff);
+ return res;
}
-
-int
-main(int argc, char **argv)
+/* 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 prob = false;
- int fd, res, calc_len, bd_len, dev_specific_param;
- int offset, j, bd_blk_len, pdt, rsp_len, vb;
+ 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 ret = 0;
+ int vb = op->verbose;
uint64_t ull;
int64_t ll;
- struct opts_t * op;
- uint8_t inq_resp[SAFE_STD_INQ_RESP_LEN];
- struct opts_t opts;
char b[80];
- op = &opts;
- memset(op, 0, sizeof(opts));
- op->cmplst = true;
+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;
+
+ 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;
@@ -866,6 +1065,7 @@ main(int argc, char **argv)
"or 1\n");
return SG_LIB_SYNTAX_ERROR;
}
+ op->cmplst_given = true;
op->cmplst = !! j;
break;
case 'd':
@@ -890,7 +1090,7 @@ main(int argc, char **argv)
break;
case 'h':
usage();
- return 0;
+ return SG_LIB_OK_FALSE;
case 'I':
op->ip_def = true;
break;
@@ -943,8 +1143,8 @@ main(int argc, char **argv)
op->rto_req = true;
break;
case 's':
- op->blk_size = sg_get_num(optarg);
- if (op->blk_size <= 0) {
+ 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;
@@ -1031,9 +1231,8 @@ main(int argc, char **argv)
#endif
if (op->version_given) {
pr2serr("sg_format version: %s\n", version_str);
- return 0;
+ return SG_LIB_OK_FALSE;
}
- vb = op->verbose;
if (NULL == op->device_name) {
pr2serr("no DEVICE name given\n\n");
usage();
@@ -1060,7 +1259,7 @@ main(int argc, char **argv)
"0)\n");
usage();
return SG_LIB_CONTRADICT;
- } else if (0 != op->blk_size) {
+ } else if (0 != op->lblk_sz) {
pr2serr("'--resize' not compatible with '--size'\n");
usage();
return SG_LIB_CONTRADICT;
@@ -1079,18 +1278,54 @@ main(int argc, char **argv)
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 /* rw=false */,
- vb)) < 0) {
+ 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));
- return sg_convert_errno(-fd);
+ ret = sg_convert_errno(-fd);
+ goto out;
}
if (op->format > 2)
goto format_only;
- ret = print_dev_id(fd, inq_resp, sizeof(inq_resp), op);
+ ret = print_dev_id(fd, inq_resp, inq_resp_sz, op);
if (ret)
goto out;
pdt = 0x1f & inq_resp[0];
@@ -1112,154 +1347,17 @@ main(int argc, char **argv)
goto format_med;
}
-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);
- ret = res;
- 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");
- goto out;
- }
- 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);
- ret = SG_LIB_WILD_RESID;
- goto out;
- }
- calc_len = 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);
- ret = -1;
- goto out;
- }
- 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);
- ret = -1;
- goto out;
- }
- 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_len) {
- pr2serr("%s: MS response length truncated (%d < %d)\n",
- __func__, rsp_len, calc_len);
+ ret = fetch_block_desc(fd, dbuff, &calc_len, &bd_lb_sz, op);
+ if (ret)
goto out;
- }
- if ((offset + bd_len) < calc_len)
- dbuff[offset + bd_len] &= 0x7f; /* clear PS bit in mpage */
- prob = false;
- bd_blk_len = 0;
- 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);
- if ((! op->long_lba) && (0xffffffff == ull)) {
- if (vb)
- pr2serr("Mode sense number of blocks maxed "
- "out, set longlba\n");
- op->long_lba = true;
- op->mode6 = false;
- op->do_rcap16 = true;
- goto again_with_long_lba;
- }
- bd_blk_len = op->long_lba ?
- sg_get_unaligned_be32(dbuff + offset + 12) :
- sg_get_unaligned_be24(dbuff + offset + 5);
- 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_blk_len, bd_blk_len);
- ll = (int64_t)ull * bd_blk_len;
- if (ll > op->total_byte_count)
- op->total_byte_count = ll;
- } else {
- printf(" No block descriptors present\n");
- prob = true;
- }
+
+ rq_lb_sz = op->lblk_sz;
if (op->resize || (op->format && ((op->blk_count != 0) ||
- ((op->blk_size > 0) && (op->blk_size != bd_blk_len))))) {
+ ((rq_lb_sz > 0) && (rq_lb_sz != bd_lb_sz))))) {
/* want to run MODE SELECT */
-
- 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");
- ret = SG_LIB_CAT_MALFORMED;
- goto out;
- }
- 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 ((op->blk_size > 0) &&
- (op->blk_size != bd_blk_len))
- /* 0 implies max capacity with new LB size */
- memset(dbuff + offset, 0, op->long_lba ? 8 : 4);
-
- if ((op->blk_size > 0) && (op->blk_size != bd_blk_len)) {
- if (op->long_lba)
- sg_put_unaligned_be32((uint32_t)op->blk_size,
- dbuff + offset + 12);
- else
- sg_put_unaligned_be24((uint32_t)op->blk_size,
- dbuff + offset + 5);
- }
if (op->dry_run) {
pr2serr("Due to --dry-run option bypass MODE "
- "SELECT(%d)command\n", (op->mode6 ? 6 : 10));
+ "SELECT(%d) command\n", (op->mode6 ? 6 : 10));
res = 0;
} else {
bool sp = true; /* may not be able to save pages */
@@ -1300,11 +1398,11 @@ again_sp_false:
}
if (res < 0)
ret = -1;
- if ((res > 0) && (bd_blk_len > 0) &&
- (res != (int)bd_blk_len)) {
+ 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_blk_len, res);
+ bd_lb_sz, res);
printf(" Probably needs format\n");
}
if ((PDT_TAPE == pdt) || (PDT_MCHANGER == pdt) ||
@@ -1382,11 +1480,17 @@ skip_f_med_reconsider:
}
out:
- 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 (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))
diff --git a/src/sg_inq.c b/src/sg_inq.c
index f2b3aa6a..b32ab879 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -49,7 +49,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.97 20180801"; /* SPC-5 rev 19 */
+static const char * version_str = "1.98 20180828"; /* SPC-5 rev 19 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -85,7 +85,7 @@ static const char * version_str = "1.97 20180801"; /* SPC-5 rev 19 */
#define VPD_DEVICE_ID 0x83
#define VPD_SOFTW_INF_ID 0x84
#define VPD_MAN_NET_ADDR 0x85
-#define VPD_EXT_INQ 0x86
+#define VPD_EXT_INQ 0x86 /* Extended Inquiry */
#define VPD_MODE_PG_POLICY 0x87
#define VPD_SCSI_PORTS 0x88
#define VPD_ATA_INFO 0x89
@@ -100,7 +100,6 @@ static const char * version_str = "1.97 20180801"; /* SPC-5 rev 19 */
#define VPD_BLOCK_LIMITS 0xb0
#define VPD_BLOCK_DEV_CHARS 0xb1
#define VPD_MAN_ASS_SN 0xb1
-#define VPD_ES_DEV_CHARS 0xb1
#define VPD_LB_PROVISIONING 0xb2
#define VPD_REFERRALS 0xb3
#define VPD_SUP_BLOCK_LENS 0xb4 /* sbc4r01 */
@@ -189,8 +188,6 @@ static struct svpd_values_name_t vpd_pg[] = {
"identification, target device only"},
#endif
{VPD_EXT_INQ, 0, -1, 0, "ei", "Extended inquiry data"},
- {VPD_ES_DEV_CHARS, 0, PDT_SES, 0, "esdc",
- "Enclosure services device characteristics (SES-4)"},
{VPD_LB_PROVISIONING, 0, 0, 0, "lbpv", "Logical block provisioning "
"(SBC)"},
{VPD_MAN_NET_ADDR, 0, -1, 0, "mna", "Management network addresses"},
@@ -1180,8 +1177,6 @@ static struct vpd_name vpd_name_arr[] = {
{VPD_REFERRALS, 0, "Referrals (sbc3)"},
{0xb0, PDT_TAPE, "Sequential access device capabilities (ssc3)"},
{0xb2, PDT_TAPE, "TapeAlert supported flags (ssc3)"},
- {VPD_ES_DEV_CHARS, PDT_SES,
- "Enclosure services device characteristics (ses4)"},
{0xb0, PDT_OSD, "OSD information (osd)"},
{0xb1, PDT_OSD, "Security token (osd)"},
/* 0xc0 to 0xff are vendor specific */
@@ -2177,8 +2172,9 @@ decode_x_inq_vpd(uint8_t * buff, int len, int do_hex)
printf(" Multi I_T nexus microcode download=%d\n", buff[9] & 0xf);
printf(" Extended self-test completion minutes=%d\n",
sg_get_unaligned_be16(buff + 10)); /* spc4r27 */
- printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n", /* spc4r32 */
- !!(buff[12] & 0x80), !!(buff[12] & 0x40), !!(buff[12] & 0x20));
+ printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
+ !!(buff[12] & 0x80), !!(buff[12] & 0x40), !!(buff[12] & 0x20),
+ !!(buff[12] & 0x10)); /* spc4r32 + 17-142r5 */
printf(" Maximum supported sense data length=%d\n",
buff[13]); /* spc4r34 */
/* All byte 14 bits added in spc5r09 */
@@ -2189,6 +2185,11 @@ decode_x_inq_vpd(uint8_t * buff, int len, int do_hex)
sg_get_unaligned_be16(buff + 15)); /* spc5r17 */
printf(" Maximum mode page change logs=%u\n",
sg_get_unaligned_be16(buff + 17)); /* spc5r17 */
+ printf(" DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
+ !!(buff[19] & 0x80), !!(buff[19] & 0x40), !!(buff[19] & 0x20),
+ !!(buff[19] & 0x10)); /* 17-142r5 */
+ printf(" DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
+ !!(buff[19] & 0x8), !!(buff[19] & 0x4), !!(buff[19] & 0x2));
}
/* VPD_SOFTW_INF_ID [0x84] */
@@ -2486,7 +2487,6 @@ decode_b0_vpd(uint8_t * buff, int len, int do_hex)
/* VPD_BLOCK_DEV_CHARS sbc */
/* VPD_MAN_ASS_SN ssc */
-/* VPD_ES_DEV_CHARS ses-4 */
static void
decode_b1_vpd(uint8_t * buff, int len, int do_hex)
{
@@ -2552,8 +2552,6 @@ decode_b1_vpd(uint8_t * buff, int len, int do_hex)
printf(" Manufacturer-assigned serial number: %.*s\n",
len - 4, buff + 4);
break;
- case PDT_SES: /* VPD_ES_DEV_CHARS implemented in sg_vpd, not here */
- /* fall through */
default:
printf(" Unable to decode pdt=0x%x, in hex:\n", pdt);
hex2stdout(buff, len, 0);
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index 35233747..a79a96ad 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -38,7 +38,7 @@
*/
-static const char * version_str = "1.45 20180628"; /* spc5r19 + sbc4r15 */
+static const char * version_str = "1.46 20180828"; /* spc5r19 + sbc4r15 */
/* standard VPD pages, in ascending page number order */
#define VPD_SUPPORTED_VPDS 0x0
@@ -48,7 +48,7 @@ static const char * version_str = "1.45 20180628"; /* spc5r19 + sbc4r15 */
#define VPD_DEVICE_ID 0x83
#define VPD_SOFTW_INF_ID 0x84
#define VPD_MAN_NET_ADDR 0x85
-#define VPD_EXT_INQ 0x86
+#define VPD_EXT_INQ 0x86 /* Extended Inquiry */
#define VPD_MODE_PG_POLICY 0x87
#define VPD_SCSI_PORTS 0x88
#define VPD_ATA_INFO 0x89
@@ -66,7 +66,6 @@ static const char * version_str = "1.45 20180628"; /* spc5r19 + sbc4r15 */
#define VPD_BLOCK_DEV_CHARS 0xb1 /* SBC-3 */
#define VPD_MAN_ASS_SN 0xb1 /* SSC-3, ADC-2 */
#define VPD_SECURITY_TOKEN 0xb1 /* OSD */
-#define VPD_ES_DEV_CHARS 0xb1 /* SES-4 */
#define VPD_TA_SUPPORTED 0xb2 /* SSC-3 */
#define VPD_LB_PROVISIONING 0xb2 /* SBC-3 */
#define VPD_REFERRALS 0xb3 /* SBC-3 */
@@ -206,8 +205,6 @@ static struct svpd_values_name_t standard_vpd_pg[] = {
{VPD_DTDE_ADDRESS, 0, 1, "dtde",
"Data transfer device element address (SSC)"},
{VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
- {VPD_ES_DEV_CHARS, 0, PDT_SES, "esdc",
- "Enclosure services device characteristics"},
{VPD_IMP_OP_DEF, 0, -1, "iod",
"Implemented operating definition (obsolete)"},
{VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
@@ -1269,6 +1266,7 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
printf(" POA_SUP=%d\n", !!(b[12] & 0x80)); /* spc4r32 */
printf(" HRA_SUP=%d\n", !!(b[12] & 0x40)); /* spc4r32 */
printf(" VSA_SUP=%d\n", !!(b[12] & 0x20)); /* spc4r32 */
+ printf(" DMS_VALID=%d\n", !!(b[12] & 0x10)); /* 17-142r5 */
printf(" Maximum supported sense data length=%d\n",
b[13]); /* spc4r34 */
printf(" IBS=%d\n", !!(b[14] & 0x80)); /* spc5r09 */
@@ -1280,6 +1278,13 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
sg_get_unaligned_be16(b + 15)); /* spc5r17 */
printf(" Maximum mode page change logs=%u\n",
sg_get_unaligned_be16(b + 17)); /* spc5r17 */
+ printf(" DM_MD_4=%d\n", !!(b[19] & 0x80)); /* 17-142r5 */
+ printf(" DM_MD_5=%d\n", !!(b[19] & 0x40)); /* 17-142r5 */
+ printf(" DM_MD_6=%d\n", !!(b[19] & 0x20)); /* 17-142r5 */
+ printf(" DM_MD_7=%d\n", !!(b[19] & 0x10)); /* 17-142r5 */
+ printf(" DM_MD_D=%d\n", !!(b[19] & 0x8)); /* 17-142r5 */
+ printf(" DM_MD_E=%d\n", !!(b[19] & 0x4)); /* 17-142r5 */
+ printf(" DM_MD_F=%d\n", !!(b[19] & 0x2)); /* 17-142r5 */
return;
}
printf(" ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
@@ -1300,8 +1305,9 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
printf(" Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
printf(" Extended self-test completion minutes=%d\n",
sg_get_unaligned_be16(b + 10)); /* spc4r27 */
- printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n", /* spc4r32 */
- !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20));
+ printf(" POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
+ !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20),
+ !!(b[12] & 0x10)); /* spc4r32 + 17-142r5 */
printf(" Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */
printf(" IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n", !!(b[14] & 0x80),
!!(b[14] & 0x40), !!(b[14] & 0x4), !!(b[14] & 0x2),
@@ -1310,6 +1316,11 @@ decode_x_inq_vpd(uint8_t * b, int len, int do_hex, bool do_long,
sg_get_unaligned_be16(b + 15)); /* spc5r17 */
printf(" Maximum mode page change logs=%u\n",
sg_get_unaligned_be16(b + 17)); /* spc5r17 */
+ printf(" DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
+ !!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20),
+ !!(b[19] & 0x10)); /* 17-142r5 */
+ printf(" DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
+ !!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2));
}
/* VPD_SOFTW_INF_ID */
@@ -2328,22 +2339,6 @@ decode_b1_vpd(uint8_t * buff, int len, int do_hex, int pdt)
printf(" Manufacturer-assigned serial number: %.*s\n",
len - 4, buff + 4);
break;
- case PDT_SES: /* T10/17-142r1 -> ses4r02 ?? */
- if (len < 8) {
- pr2serr("Enclosure service device characteristics VPD page "
- "length too short=%d\n", len);
- return;
- }
- printf(" SESDNLD=%d\n", !! (0x2 & buff[4]));
- printf(" SPCDNLD=%d\n", !! (0x1 & buff[4]));
- printf(" DMAS=%d\n", !! (0x80 & buff[6]));
- printf(" DMSAS=%d\n", !! (0x40 & buff[6]));
- printf(" DMOAS=%d\n", !! (0x20 & buff[6]));
- printf(" DMOSAS=%d\n", !! (0x10 & buff[6]));
- printf(" DMOSASDS=%d\n", !! (0x8 & buff[6]));
- printf(" DMOSDS=%d\n", !! (0x4 & buff[6]));
- printf(" ADMS=%d\n", !! (0x1 & buff[6]));
- break;
default:
pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
hex2stderr(buff, len, 0);
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index 98a7ca15..5facfc84 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -1903,7 +1903,7 @@ main(int argc, char * argv[])
ret = res;
fini:
- /* file handles not explicity closed; let process cleanup do that */
+ /* file handles not explicitly closed; let process cleanup do that */
if (0 == verbose) {
if (! sg_if_can2stderr("sg_xcopy failed: ", ret))
pr2serr("Some error occurred, try again with '-v' or '-vv' for "