aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog2
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg3_utils.85
-rw-r--r--doc/sg_write_x.8288
-rw-r--r--include/sg_lib.h1
-rw-r--r--include/sg_pt_linux.h16
-rw-r--r--include/sg_pt_nvme.h56
-rw-r--r--include/sg_unaligned.h4
-rw-r--r--lib/sg_cmds_basic2.c2
-rw-r--r--lib/sg_lib.c14
-rw-r--r--lib/sg_lib_data.c2
-rw-r--r--lib/sg_pt_freebsd.c725
-rw-r--r--lib/sg_pt_linux.c4
-rw-r--r--lib/sg_pt_linux_nvme.c205
-rw-r--r--lib/sg_pt_win32.c137
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/sg_inq.c51
-rw-r--r--src/sg_ses.c6
-rw-r--r--src/sg_write_x.c1475
19 files changed, 2135 insertions, 862 deletions
diff --git a/ChangeLog b/ChangeLog
index 0ab13cd3..91dfb821 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 [20171209] [svn: r736]
+Changelog for sg3_utils-1.43 [20171219] [svn: r737]
- sg_bg_ctl: new Background control command (sbc4r08)
- sg_write_x: where x can be normal, atomic, orwrite,
same, scattered, or stream writes with 16 or 32 byte
diff --git a/debian/changelog b/debian/changelog
index 64d72ec5..5dbcd649 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> Mon, 04 Dec 2017 15:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Mon, 18 Dec 2017 14:00:00 -0500
sg3-utils (1.42-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index ceded7b7..8472d58b 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -314,6 +314,11 @@ problems with protection information (PI or DIF). For example this error
may occur when reading a block on a drive that has never been written (or
is unmapped) if that drive was formatted with type 1, 2 or 3 protection.
.TP
+.B 48
+this is an internal message indicating a NVMe status field (SF) is other
+than zero after a command has been executed (i.e. something went wrong).
+Work in this area is currently experimental.
+.TP
.B 49
low level driver reports a residual count (i.e. number of bytes actually
received by HBA is 'requested_bytes - residual_count') that is high enough
diff --git a/doc/sg_write_x.8 b/doc/sg_write_x.8
index 760c0d6f..73fd8c30 100644
--- a/doc/sg_write_x.8
+++ b/doc/sg_write_x.8
@@ -3,8 +3,8 @@
sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
.SH SYNOPSIS
.B sg_write_x
-[\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-\tag=AT\fR] [\fI\-\-atomic=AB\fR]
-[\fI\-\-bmop=OP,PGP\fR] [\fI\-\-bs=LBS\fR] [\fI\-\-combined=DOF\fR]
+[\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app\-tag=AT\fR] [\fI\-\-atomic=AB\fR]
+[\fI\-\-bmop=OP,PGP\fR] [\fI\-\-bs=BS\fR] [\fI\-\-combined=DOF\fR]
[\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR] [\fI\-\-dry\-run\fR] [\fI\-\-fua\fR]
[\fI\-\-generation=EOG,NOG\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-help\fR]
\fI\-\-in=IF\fR [\fI\-\-lba=LBA[,LBA...]\fR] [\fI\-\-normal\fR]
@@ -16,48 +16,48 @@ sg_write_x \- SCSI WRITE normal/ATOMIC/SAME/SCATTERED/STREAM, ORWRITE commands
[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
-\fI\-\-normal\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app\-tag=AT\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
-[\fI\-\-grpnum=GN\fR] \fI\-\-in=IF\fR [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
+\fI\-\-normal\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
+[\fI\-\-app\-tag=AT\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR]
+[\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
[\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-ref\-tag=RT\fR] [\fI\-\-strict\fR]
[\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=WPR\fR]
\fIDEVICE\fR
.PP
.B sg_write_x
-\fI\-\-or\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-bmop=OP,PGP\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR]
-\fI\-\-in=IF\fR [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
-[\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-strict\fR] [\fI\-\-timeout=TO\fR]
-[\fI\-\-wrprotect=OPR\fR] \fIDEVICE\fR
+\fI\-\-or\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
+[\fI\-\-bmop=OP,PGP\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
+[\fI\-\-generation=EOG,NOG\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-lba=LBA\fR]
+[\fI\-\-num=NUM\fR] [\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-strict\fR]
+[\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=OPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
-\fI\-\-atomic=AB\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR]
-\fI\-\-in=IF\fR [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
+\fI\-\-atomic=AB\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
+[\fI\-\-app-tag=AT\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
+[\fI\-\-grpnum=GN\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
[\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-ref\-tag=RT\fR] [\fI\-\-strict\fR]
-[\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=OPR\fR] \fIDEVICE\fR
+[\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
\fI\-\-same=NDOB\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR]
+[\fI\-\-bs=BS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR]
[\fI\-\-in=IF\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
[\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-ref\-tag=RT\fR] [\fI\-\-strict\fR]
[\fI\-\-timeout=TO\fR] [\fI\-\-unmap=U_A\fR]
-[\fI\-\-wrprotect=OPR\fR] \fIDEVICE\fR
+[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
-\fI\-\-scattered=RD\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
-[\fI\-\-grpnum=GN\fR] \fI\-\-in=IF\fR [\fI\-\-lba=LBA[,LBA...]\fR]
+\fI\-\-scattered=RD\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
+[\fI\-\-app-tag=AT\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dld=DLD\fR] [\fI\-\-dpo\fR]
+[\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-lba=LBA[,LBA...]\fR]
[\fI\-\-num=NUM[,NUM...]\fR] [\fI\-\-offset=OFF[,DLEN]\fR]
[\fI\-\-ref\-tag=RT\fR] [\fI\-\-scat\-file=SF\fR] [\fI\-\-scat\-raw\fR]
[\fI\-\-strict\fR] [\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR]
[\fI\-\-wrprotect=WPR\fR] \fIDEVICE\fR
.PP
.B sg_write_x
-\fI\-\-stream=ID\fR [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-app-tag=AT\fR]
-[\fI\-\-bs=LBS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-grpnum=GN\fR]
-\fI\-\-in=IF\fR [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
+\fI\-\-stream=ID\fR \fI\-\-in=IF\fR [\fI\-\-16\fR] [\fI\-\-32\fR]
+[\fI\-\-app-tag=AT\fR] [\fI\-\-bs=BS\fR] [\fI\-\-dpo\fR] [\fI\-\-fua\fR]
+[\fI\-\-grpnum=GN\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-num=NUM\fR]
[\fI\-\-offset=OFF[,DLEN]\fR] [\fI\-\-ref\-tag=RT\fR] [\fI\-\-strict\fR]
[\fI\-\-tag\-mask=TM\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-wrprotect=WPR\fR]
\fIDEVICE\fR
@@ -69,16 +69,17 @@ ATOMIC, WRITE SAME, WRITE SCATTERED or WRITE STREAM. This utility supports
the 16 and 32 byte variants of all six commands. Hence some closely related
commands are not supported (e.g. WRITE(10)). All 32 byte variants, apart from
ORWRITE(32), require the \fIDEVICE\fR to be formatted with type 1, 2 or 3
-protection information (PI), making all logical blocks 8 bytes longer on the
-media.
+Protection Information (PI), making all logical blocks 8 bytes (or a multiple
+of 8 bytes) longer on the media.
.PP
The command line interface is a little crowded with over thirty options. Hence
the SYNOPSIS, after listing all the (long) options, lists those applicable
to each supported command. For each command synopsis, the option that selects
-the SCSI command is shown first. If no command option is given then a "normal"
-WRITE is assumed. Even though the \fI\-\-scat\-file=SF\fR option can be
-given for every command, it is only shown for WRITE SCATTERED where it is
-most useful. If the \fI\-\-scat\-file=SF\fR option is given then neither the
+the SCSI command is shown first followed by any required options. If no
+command option is given then a "normal" WRITE is assumed. Even though the
+\fI\-\-scat\-file=SF\fR option can be given for every command, it is only
+shown for WRITE SCATTERED where it is most useful. If the
+\fI\-\-scat\-file=SF\fR option is given then neither the
\fI\-\-lba=LBA[,LBA...]\fR nor the \fI\-\-num=NUM[,NUM...]\fR options
should be given. Only the first item of the \fI\-\-lba=LBA[,LBA...]\fR and
the \fI\-\-num=NUM[,NUM...]\fR options (or first pair (or quintet) from the
@@ -87,9 +88,9 @@ command. All commands can take \fI\-\-dry\-run\fR and \fI\-\-verbose\fR in
addition to those shown in the SYNOPSIS.
.PP
The logical block size in bytes can be given explicitly with the
-\fI\-\-bs=LBS\fR option, as long as \fILBS\fR is greater than zero. It
-is typically a power of tw0, 512 or greater. If the \fI\-\-bs=LBS\fR option
-is not given or \fILBS\fR is zero then the SCSI READ CAPACITY command is
+\fI\-\-bs=BS\fR option, as long as \fIBS\fR is greater than zero. It
+is typically a power of two, 512 or greater. If the \fI\-\-bs=BS\fR option
+is not given or \fIBS\fR is zero then the SCSI READ CAPACITY command is
used to find the logical block size. First the READ CAPACITY(16) command is
tried and if successful the logical block size in the response is typically
used as the actual block size for this utility. The exception is when
@@ -112,17 +113,18 @@ Attempts to write multi megabyte data with a single command are likely to fail
for one of several reasons. First the operating system might object to
allocating a buffer that large. Next the SCSI pass\-through usually limits
data blocks to a few megabytes or less. Finally the storage device might
-have a limited amount of RAM to support an write operation such as atomic (as
+have a limited amount of RAM to support a write operation such as atomic (as
it may need to roll back). The storage device can inform the application
client of its limitations via the block limits VPD page (0xb0), with the
maximum atomic transfer length field amongst others.
.PP
-A degenerate LBA range descriptor with no PI has an LBA and NUM of zero.
-A degenerate LBA range descriptor with PI additionally has its RT, AT and
-TM fields set to zero. They are degenerate in the sense that they are
-indistinguishable from a pad of zeros that follow the scatter list in the
-data\-out buffer. SBC\-4 makes clear that the degenerate LBA range descriptor
-is valid. This may become an issue if \fIRD\fR given in the
+A degenerate LBA (Logical Block Address) range descriptor with no PI has
+an LBA and NUM of zero. A degenerate LBA range descriptor with PI
+additionally has its RT, AT and TM fields set to zero (note: that is not
+the default values for RT, AT and TM). They are degenerate in the sense
+that they are indistinguishable from a pad of zeros that follow the scatter
+list in the data\-out buffer. SBC\-4 makes clear that a degenerate LBA
+range descriptor is valid. This may become an issue if \fIRD\fR given in the
\fI\-\-scattered=RD\fR option has the value 0. In this case the logic may
need to scan the user provided data to calculate the number of LBA
range descriptors which is required by the WRITE SCATTERED cdb. In the
@@ -130,8 +132,8 @@ absence of other information the logic will take a degenerate LBA range
descriptor as a terminator of the scatter list.
.PP
The current reference for these commands is draft SBC\-4 (T10/BSR INCITS
-506) revision 15 dated 9 November 2017. All six SCSI commands are
-described in that document.
+506) revision 15 dated 9 November 2017. All six SCSI commands are described
+in that document.
.SH OPTIONS
Arguments to long options are mandatory for short options as well.
The options are arranged in alphabetical order based on the long
@@ -153,7 +155,7 @@ from ORWRITE(32) all other 32 byte cdb variants require a \fIDEVICE\fR
formatted with type 1, 2 or 3 protection information.
.TP
\fB\-a\fR, \fB\-\-app\-tag\fR=\fIAT\fR
-where \fIAT\fR is the expected logical block application tag field found in
+where \fIAT\fR is the "expected logical block application tag" field found in
most of the 32 byte cdb variants (the exception is ORWRITE(32)). \fIAT\fR is
a 16 bit field which means the maximum value is 0xffff. The default value
is 0xffff .
@@ -170,35 +172,48 @@ BMOP and 'Previous Generation Processing' fields respectively. BMOP is
a 3 bit field (ranges from 0 to 7) and PGP is a 4 bit field (ranges from
0 to 15). Both fields default to 0.
.TP
-\fB\-b\fR, \fB\-\-bs\fR=\fILBS\fR
-where \fILBS\fR is the logical block size which is usually the same as
-the logical block size of the \fIDEVICE\fR. The exception is outlined in
-the DESCRIPTION section above. The default value is zero. If this option
-is not given or is given with a \fILBS\fR of zero then the SCSI READ
+\fB\-b\fR, \fB\-\-bs\fR=\fIBS\fR
+where \fIBS\fR is the logical block size or the actual block size which
+will be slightly bigger. The default value is zero. If this option
+is not given or is given with a \fIBS\fR of zero then the SCSI READ
CAPACITY(16) command is sent to \fIDEVICE\fR. If that fails then the READ
-CAPACITY(10) command is sent.
+CAPACITY(10) command is sent. The logical and actual block size will be
+derived from the response of the READ CAPACITY command.
+.br
+This section assumes \fIBS\fR is greater than zero. If \fIBS\fR is less than
+512 (bytes) or not a multiple of 8, a warning is issued and the utility
+continues unless the \fI\-\-strict\fR option is also given. If \fIBS\fR
+is a power of two (e.g. 512) then the logical and actual block size is
+set to \fIBS\fR (e.g. 512). If \fIBS\fR is not a power of two (e.g. 520)
+then the logical block size is set to the closest power of two less than
+\fIBS\fR (e.g. 512) and the actual block size is set to \fIBS\fR (e.g.
+520).
+.br
+If the logical and actual block size are different then a later check
+will reduce the actual block size back to the logical block size unless
+\fI\-\-wrprotect=WPR\fR is greater than zero.
.TP
\fB\-c\fR, \fB\-\-combined\fR=\fIDOF\fR
This option only applies to WRITE SCATTERED and assumes the whole data\-out
buffer can be read from \fIIF\fR given by the \fI\-\-in=IF\fR option. The
-whole data\-out buffer is the parameter list header, followed by one or more
-LBA range descriptors, optional followed by some pad bytes and then the data
-to be written to the media. If the \fI\-\-lba=LBA[,LBA...]\fR,
-\fI\-\-num=NUM[,NUM...]\fR or \fI\-\-scat\-file=SF\fR options are given
+whole data\-out buffer is the parameter list header, followed by zero or more
+LBA range descriptors, optionally followed by some pad bytes and then the
+data to be written to the media. If the \fI\-\-lba=LBA[,LBA...]\fR,
+\fI\-\-num=NUM[,NUM...]\fR or \fI\-\-scat\-file=SF\fR options are also given
then an error is generated. The \fIDOF\fR argument should be the value
suitable for the 'Logical Block Data Offset' field in the WRITE SCATTERED
cdb. This is the offset in the data\-out buffer where the data to write
to the media commences. The unit of that field is the actual block size
-which is the logical block size plus 8, if protection information (PI) is
-being sent. When \fIWPR\fR (from \fI\-\-wrprotect=WPR\fR) is greater than
-zero then PI is expected. SBC\-4 revision 15 does not state it but it would
-appear that a \fIDOF\fR value of 0 is invalid. It is suggested that this
-option be used with the \fI\-\-strict\fR option while experimenting as
-random or incorrect data fed in via the \fI\-\-in=IF\fR option could write
-a lot of "interesting" data on the \fIDEVICE\fR. If \fIDOF\fR is given as 0
-the utility will scan the data in \fIIF\fR until \fIRD\fR LBA range
-descriptors are found; or if \fIRD\fR is also 0 until a degenerate LBA range
-descriptor is found.
+which is the logical block size plus a multiple of 8, if protection
+information (PI) is being sent. When \fIWPR\fR (from \fI\-\-wrprotect=WPR\fR)
+is greater than zero then PI is expected. SBC\-4 revision 15 does not state
+it but it would appear that a \fIDOF\fR value of 0 is invalid. It is
+suggested that this option be used with the \fI\-\-strict\fR option while
+experimenting as random or incorrect data fed in via the \fI\-\-in=IF\fR
+option could write a lot of "interesting" data all over the \fIDEVICE\fR.
+If \fIDOF\fR is given as 0 the utility will scan the data in \fIIF\fR until
+\fIRD\fR LBA range descriptors are found; or if \fIRD\fR is also 0 until a
+degenerate LBA range descriptor is found.
.TP
\fB\-D\fR, \fB\-\-dld\fR=\fIDLD\fR
where \fIDLD\fR is the duration limits descriptor spread across 3 bits in
@@ -210,8 +225,8 @@ cdbs is set if (0x4 & \fIDLD\fR) is non\-zero.
.TP
\fB\-d\fR, \fB\-\-dpo\fR
if this option is given then the DPO (disable page out) bit field in the
-cdb is set. The default is to clear this bit field. Applies to all write
-commands except WRITE SAME.
+cdb is set. The default is to clear this bit field. Applies to all
+commands supported by thus utility except WRITE SAME.
.TP
\fB\-x\fR, \fB\-\-dry\-run\fR
this option exits (with a status of 0) just before it would otherwise send
@@ -225,8 +240,8 @@ status value.
.TP
\fB\-f\fR, \fB\-\-fua\fR
if this option is given then the FUA (force unit access) bit field in the
-cdb is set. The default is to clear this bit field. Applies to all write
-commands except WRITE SAME.
+cdb is set. The default is to clear this bit field. Applies to all
+commands supported by thus utility except WRITE SAME.
.TP
\fB\-G\fR, \fB\-\-generation\fR=\fIEOG,NOG\fR
the arguments for this option are used by the ORWITE(32) command only.
@@ -250,17 +265,22 @@ SCATTERED(16 or 32) which may include other data in the data\-out buffer.
For WRITE SCATTERED (16 or 32) the data\-out buffer is made up of 3 or 4
components in this order: a parameter list header (32 zero bytes); zero or
more LBA range descriptors, optionally some pad bytes (zeros) and then data
-to write to the media. For WRITE
-SCATTERED \fIIF\fR only provides the data to write to the media unless
-\fI\-\-combined=DOF\fR is given. When the \fI\-\-combined=DOF\fR option is
-given \fIIF\fR contains all 3 components of the WRITE SCATTERED data\-out
-buffer in binary. The data read from \fIIF\fR starts from byte offset
-\fIOFF\fR which defaults to zero and no more than \fIDLEN\fR bytes are read
-from that point (i.e. the file byte offset \fIOFF\fR). If \fIDLEN\fR is
-zero or not given the rest of the file \fIIF\fR is read. This option is
-mandatory apart from when \-\-same=1 is given (that sets the NDOB bit which
-stands for "No Data Out Buffer"). In Unix based OSes, any number of zeros
-can produced by using the /dev/zero device file.
+to write to the media. For WRITE SCATTERED \fIIF\fR only provides the data
+to write to the media unless \fI\-\-combined=DOF\fR is given. When the
+\fI\-\-combined=DOF\fR option is given \fIIF\fR contains all components of
+the WRITE SCATTERED data\-out buffer in binary. The data read from \fIIF\fR
+starts from byte offset \fIOFF\fR which defaults to zero and no more than
+\fIDLEN\fR bytes are read from that point (i.e. from the file byte offset
+\fIOFF\fR). If \fIDLEN\fR is zero or not given the rest of the file \fIIF\fR
+is read. This option is mandatory apart from when \-\-same=1 is given (that
+sets the NDOB bit which stands for "No Data Out Buffer"). In Unix based
+OSes, any number of zeros can be produced by using the /dev/zero device file.
+.br
+\fIIF\fR may be "\-" which is taken as stdin. In this case the
+\fI\-\-offset=OFF,DLEN\fR can be given with \fIOFF\fR set to 0 and
+\fILEN\fR set to a non-zero value, preferably a multiple of the actual block
+size. The utility can also deduce how long the \fIIF\fR should be from
+\fINUM\fR (or the sum of them in the case of a scatter list).
.TP
\fB\-l\fR, \fB\-\-lba\fR=\fILBA[,LBA...]\fR
where the argument is a single Logical Block Address (LBA) or a comma
@@ -270,17 +290,18 @@ can usefully take more than one \fILBA\fR. Whatever number of \fILBA\fRs is
given, there needs to be an equal number of \fINUM\fRs given to the
\fI\-\-num=NUM[,NUM...]\fR option. The first given \fILBA\fR joins with the
first given \fINUM\fR to form the first LBA range descriptor (which T10
-number from zero in SBC\-4). The second \fILBA\ffR joins with the second
+number from zero in SBC\-4). The second \fILBA\fR joins with the second
\fILBA\fR to form the second LBA range descriptor, etc. A more convenient
-way to define a large number of LBA range descriptors is the \fISF\fR
-name of the file given with the \fI\-\-scat\-file=SF\fR option. Defaults
-to logical block 0 (which could be dangerous) while \fINUM\fR defaults to
-0 which makes the combination harmless. \fILBA\fR is assumed to be in decimal
-unless prefixed with '0x' or has a trailing 'h'.
+way to define a large number of LBA range descriptors is with the
+\fI\-\-scat\-file=SF\fR option. Defaults to logical block 0 (which could be
+dangerous) while \fINUM\fR defaults to 0 which makes the combination harmless.
+\fILBA\fR is assumed to be in decimal unless prefixed with '0x' or has a
+trailing 'h'.
.TP
\fB\-N\fR, \fB\-\-normal\fR
-as well as implicitly selecting a "normal" WRITE (16 or 32) in the absence
-of selecting any other command, the choice of a "normal" WRITE can be made
+the choice of a "normal" WRITE (16 or 32) command can be made explicitly
+with this option. In the absence of selecting any other command (e.g.
+\fI\-\-atomic=AB\fR ), the choice of a "normal" WRITE is the default.
.TP
\fB\-n\fR, \fB\-\-num\fR=\fINUM[,NUM...]\fR
where the argument is a single NUMber of blocks (NUM) or a comma separated
@@ -299,7 +320,7 @@ reading from. The default value of \fIOFF\fR is zero which is the beginning
of file named \fIIF\fR. \fIDLEN\fR is the maximum number of bytes to read,
starting at byte offset \fIOFF\fR, from the file named \fIIF\fR. Less bytes
will be read if an end of file occurs before \fIDLEN\fR is exhausted. If
-\fIDLEN\fR is zer or not given then reading from byte offset \fIOFF\fR to
+\fIDLEN\fR is zero or not given then reading from byte offset \fIOFF\fR to
the end of the file named \fIIF\fR is assumed.
.TP
\fB\-O\fR, \fB\-\-or\fR
@@ -312,42 +333,44 @@ command in this utility that does not require a \fIDEVICE\fR formatted with
type 1, 2 or 3 PI (although it will still work if it is formatted with PI).
.TP
\fB\-r\fR, \fB\-\-ref\-tag\fR=\fIRT\fR
-where \fIRT\fR is the expected logical block reference tag field found in
-the 32 byte cdb variants of WRITE, WRITE ATOMIC, WRITE SAME and WRITE STREAM.
-The field is also found in the WRITE SCATTERED(32) LBA range descriptors.
-It is 32 bit field which means the maximum value is 0xffffffff. The default
-value is 0xffffffff.
+where \fIRT\fR is the "expected initial logical block reference tag" field
+found in the 32 byte cdb variants of WRITE, WRITE ATOMIC, WRITE SAME and
+WRITE STREAM. The field is also found in the WRITE SCATTERED(32) LBA range
+descriptors. It is a 32 bit field which means the maximum value is
+0xffffffff. The default value is 0xffffffff.
.TP
\fB\-S\fR, \fB\-\-same\fR=\fINDOB\fR
selects the WRITE SAME command with the NDOB field set to \fINDOB\fR which
-stands for No Data\-Out Buffer. \fINDOB\fR can take values 0 or 1 (i.e. i
-is a single bit field). When \-\-same=1 are options associated with the
+stands for No Data\-Out Buffer. \fINDOB\fR can take values 0 or 1 (i.e. it
+is a single bit field). When \-\-same=1 all options associated with the
data\-out buffer are ignored.
.TP
\fB\-q\fR, \fB\-\-scat\-file\fR=\fISF\fR
where \fISF\fR is the name of an auxiliary file containing the scatter list
for the WRITE SCATTERED command. If the \fI\-\-scat\-raw\fR option is also
given then \fISF\fR is assumed to contain both the parameter list header (32
-bytes of zeros) followed by one or more LBA range descriptors which are
+bytes of zeros) followed by zero or more LBA range descriptors which are
also 32 bytes long each. These components are as defined by SBC\-4 (i.e.
in binary with integers in big endian format). If the \fI\-\-scat\-raw\fR
option is not given then a file of ACSII hexadecimal is expected as described
in the SCATTERED FILE ASCII FORMAT section below.
.br
If this option is given with the \fI\-\-combined=DOF\fR option then this
-utility will exit with a syntax error.
+utility will exit with a syntax error. \fISF\fR must not be "\-", a way
+of stopping the user trying to redirect stdin.
.TP
\fB\-R\fR, \fB\-\-scat\-raw\fR
this option only effects the way that the file named \fISF\fR from the
\fI\-\-scat\-file=SF\fR option for WRITE SCATTERED is interpreted. By
default (i.e. without this option), \fISF\fR is parsed as ASCII hexadecimal
-with blank lines and lines contents from and including '#' to the end of
+with blank lines and line contents from and including '#' to the end of
line ignored. Hence it can contain comments and other indications. When
this option is given, the file named \fISF\fR is interpreted as binary.
-It is assumed to contain 32 bytes of zeros (the WRITE SCATTERED parameter
-list header) followed by one or more LBA range descriptors (which are 32
-bytes each). If the \fI\-\-strict\fR option is given the reserved field
-in those two items are checked with any non zero bytes causing an error.
+As binary it is assumed to contain 32 bytes of zeros (the WRITE SCATTERED
+parameter list header) followed by zero or more LBA range descriptors (which
+are 32 bytes each). If the \fI\-\-strict\fR option is given the reserved
+field in those two items are checked with any non zero bytes causing an
+error.
.TP
\fB\-S\fR, \fB\-\-scattered\fR=\fIRD\fR
selects the WRITE SCATTERED command with \fIRD\fR being the number of LBA
@@ -360,6 +383,15 @@ is made up of an 8 byte LBA, followed by a 4 byte number_of_blocks followed
by 20 bytes of zeros. In the 32 byte variant the LBA and number_of_blocks
are followed by a RT (4 bytes), an AT (2 bytes) and a TM (2 bytes) then
12 bytes of zeros.
+.br
+This paragraph applies when \fIRD\fR is greater than zero.
+If \fIRD\fR is less than the number of LBA range descriptors built from
+command line options, from the \fI\-\-scat\-file=SF\fR option or
+decoded from \fIIF\fR (when the \fI\-\-combined=DOF\fR option is given)
+then \fIRD\fR takes precedence; so \fIRD\fR is placed in the "Number of
+LBA Range Descriptors" field in the cdb. If \fIRD\fR is greater than
+the number of LBA range descriptors found from the provided data and
+options, then an error is generated.
.TP
\fB\-T\fR, \fB\-\-stream\fR=\fIID\fR
selects the WRITE STREAM command with the STR_ID field set to \fIID\fR.
@@ -372,17 +404,24 @@ stderr and an indicative exit status. While experimenting with these commands,
especially WRITE SCATTERED, it is recommended to use this option.
.TP
\fB\-t\fR, \fB\-\-tag\-mask\fR=\fITM\fR
-where \fITM\fR is the logical block application tag mask field in the
-WRITE ATOMIC(32) and WRITE(32) cdbs. It is 16 bit field which means the
-maximum value is 0xffff. The default value is 0xffff.
+where \fITM\fR is the "logical block application tag mask" field found in the
+32 byte cdb variants of WRITE, WRITE ATOMIC, WRITE SAME and WRITE STREAM. The
+field is also found in the WRITE SCATTERED(32) LBA range descriptors. It is a
+16 bit field which means the maximum value is 0xffff. The default value is
+0xffff.
.TP
\fB\-I\fR, \fB\-\-timeout\fR=\fITO\fR
where \fITO\fR is the command timeout value in seconds. The default value is
-120 seconds. If \fINUM\fR is large a WRITE ATOMIC command may require
-considerably more time than 120 seconds to complete.
+120 seconds. If \fINUM\fR is large on slow media then these WRITE commands
+may require considerably more time than 120 seconds to complete.
.TP
\fB\-u\fR, \fB\-\-unmap\fR=\fIU_A\fR
-where \fITO\fR is the command timeout value in seconds. The default value is
+where \fIU_A\fR is OR-ed bit values used to set the UNMAP and ANCHOR bit
+fields in the WRITE SAME (16 or 32) cdb. If \fIU_A\fR is 1 then the UNMAP
+bit field is set; if \fIU_A\fR is 2 then the ANCHOR bit field is set; if
+\fIU_A\fR is 3 then both the UNMAP and ANCHOR bit fields are set. The
+default value for both bit fields is clear (0); setting \fIU_A\fR to 0 will
+also clear both bit fields.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
increase the degree of verbosity (debug messages). These messages are usually
@@ -393,17 +432,19 @@ output version string then exit.
.TP
\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWPR\fR
sets the WRPROTECT field (3 bits) in all sg_write_x commands apart from
-ORWRITE which has a 3 bit ORPROTECT field. In all cases \fIWPR\fR is placed
+ORWRITE which has a 3 bit ORPROTECT field (and the synopsis shows \fiOPR\fR
+to highlight the difference). In all cases \fIWPR\fR is placed
in that 3 bit field. The default value is zero which does not send any PI
in the data\-out buffer. \fIWPR\fR should be a value between 0 and 7.
.SH SCATTERED FILE ASCII FORMAT
-This file named with the \fI\-\-scat\-file=SF\fR option only applies to
-the WRITE SCATTERED (16 and 32) command. If the \fI\-\-scat\-raw\fR option
-is also given then the file named \fISF\fR is expected to be binary and
-contain the parameter list header (32 bytes of zeros for both the 16 and 32
-byte variants) followed by "n" LBA range descriptors, each of 32 bytes each.
-This section describes what is expected in \fISF\fR when the
-\fI\-\-scat\-raw\fR option is not given.
+All commands in this utility can take a \fI\-\-scat\-file=SF\fR and that
+option can be seen as a replacement for the \fI\-\-lba=LBA[,LBA...]\fR and
+\fI\-\-num=NUM[,NUM...]\fR options. if both the \fI\-\-scat\-file=SF\fR and
+\fI\-\-scat\-raw\fR options are given then the file named \fISF\fR is
+expected to be binary and contain the parameter list header (32 bytes of
+zeros for both the 16 and 32 byte variants) followed by zero or more LBA
+range descriptors, each of 32 bytes each. This section describes what is
+expected in \fISF\fR when the \fI\-\-scat\-raw\fR option is not given.
.PP
The ASCII hexadecimal "scatter file" (named by \fISF\fR) can contain
comments, empty lines and numbers. If multiple numbers appear on one line
@@ -434,7 +475,6 @@ given, and if no more items are on the line then RT, AT and TM are given
their default values (all "ff" bytes). Spaces and tabs may appear between
items but commas are the separators. Two commas with no value between them
will cause the "missing" item to receive its default value.
-xxxxx
.SH NOTES
Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
@@ -453,7 +493,7 @@ the sg3_utils(8) man page.
One simple usage is to write 4 blocks of zeros from (and including) a given
LBA:
.PP
- sg_write_x \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4 /dev/sdc
+ sg_write_x \-\-atomic=0 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4 /dev/sdc
.PP
Since \fI\-\-bs=BS\fR has not been given, then this utility will call the
READ CAPACITY(16) command on /dev/sdc to determine the number of bytes in a
@@ -464,21 +504,25 @@ zeros (each block containing 512 bytes) will be written from (and including)
LBA 0x1234 . Now to bypass the need for the READ CAPACITY command(s) the
\fI\-\-bs=BS\fR option can be used:
.PP
- sg_write_atomic \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4
+ sg_write_x \-\-atomic=0 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4
/dev/sdc
.PP
Both of the examples above issue a SCSI WRITE ATOMIC(16) command. To send the
32 byte variant add \-\-32 as in:
.PP
- sg_write_atomic \-\-32 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234 \-\-num=4
-/dev/sdc
+ sg_write_x \-\-atomic=0 \-\-32 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234
+\-\-num=4 /dev/sdc
+.PP
+To send a WRITE STREAM(32) with a STR_ID of 1 use the following:
.PP
-To drop the WRITE ATOMIC(32) and replace it with a normal WRITE(32) add the
-\-\-non\-atomic option:
+ sg_write_x \-\-stream=1 \-\-32 \-\-bs=512 \-\-in=/dev/zero \-\-lba=0x1234
+\-\-num=4 /dev/sdc
.PP
- sg_write_atomic \-\-non\-atomic \-\-32 \-\-bs=512 \-\-in=/dev/zero
-\-\-lba=0x1234 \-\-num=4 /dev/sdc
+Example of a WRITE SCATTERED(16) command with the scatter list in
+scat_file.txt
+ sg_write_x \-\-scattered=3 \-q scat_file.txt \-i /dev/zero /dev/sg
.PP
+xxxxxx More examples ...
.SH AUTHORS
Written by Douglas Gilbert.
.SH "REPORTING BUGS"
diff --git a/include/sg_lib.h b/include/sg_lib.h
index da51749e..c5230974 100644
--- a/include/sg_lib.h
+++ b/include/sg_lib.h
@@ -363,6 +363,7 @@ void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
#define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */
#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */
/* [sk,asc,ascq: 0xb,0x10,*] */
+#define SG_LIB_NVME_STATUS 48 /* NVMe Status Field (SF) other than 0 */
#define SG_LIB_WILD_RESID 49 /* Residual value for data-in transfer of a */
/* SCSI command is nonsensical */
#define SG_LIB_OS_BASE_ERR 50 /* in Linux: values found in: */
diff --git a/include/sg_pt_linux.h b/include/sg_pt_linux.h
index 4e68f286..4b59cfbf 100644
--- a/include/sg_pt_linux.h
+++ b/include/sg_pt_linux.h
@@ -93,24 +93,28 @@ struct sg_io_v4 {
struct sg_pt_linux_scsi {
struct sg_io_v4 io_hdr; /* use v4 header as it is more general */
- int dev_fd; /* -1 if not given (yet) */
- int in_err;
- int os_err;
- unsigned char tmf_request[4];
+ /* Leave io_hdr in first place of this structure */
bool is_sg;
bool is_bsg;
bool is_nvme;
bool mdxfer_out; /* direction of metadata xfer, true->data-out */
bool scsi_dsense; /* SCSI descriptor sense active when true */
+ int dev_fd; /* -1 if not given (yet) */
+ int in_err;
+ int os_err;
uint32_t nvme_nsid; /* 1 to 0xfffffffe are possibly valid, 0
* implies dev_fd is not a NVMe device
* (is_nvme=false) or it is a NVMe char
* device (e.g. /dev/nvme0 ) */
- uint32_t nvme_result;
+ uint32_t nvme_result; /* DW0 from completion queue */
+ uint32_t nvme_status; /* SF: DW3 31:17 from completion queue, mask
+ * with 0x3ff for message lookup, may be
+ * OR-ed with 0x4000 for DNR (Do Not Retry) */
uint32_t mdxfer_len;
void * mdxferp;
- uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
+ uint8_t * nvme_id_ctlp; /* cached response to controller IDENTIFY */
uint8_t * free_nvme_id_ctlp;
+ unsigned char tmf_request[4];
};
struct sg_pt_base {
diff --git a/include/sg_pt_nvme.h b/include/sg_pt_nvme.h
index a3133773..25d52021 100644
--- a/include/sg_pt_nvme.h
+++ b/include/sg_pt_nvme.h
@@ -18,6 +18,20 @@ extern "C" {
/* structures copied and slightly modified from <linux/nvme_ioctl.h> which
* is Copyright (c) 2011-2014, Intel Corporation. */
+
+/* Note that the command input structure is in (packed) "cpu" format. That
+ * means, for example, if the CPU is little endian (most are) then so is the
+ * structure. However what comes out in the data-in buffer (e.g. for the
+ * Admin Identify command response) is almost all little endian following ATA
+ * (but no SCSI and IP which are big endian) and Intel's preference. There
+ * are exceptions, for example the EUI-64 identifiers in the Admin Identify
+ * response are big endian.
+ *
+ * Code online (e.g. nvme-cli at github.com) seems to like packed strcutures,
+ * the author prefers byte offset plus a range of unaligned integer builders
+ * such as those in sg_unaligned.h .
+ */
+
#ifdef __GNUC__
#ifndef __clang__
struct __attribute__((__packed__)) sg_nvme_user_io
@@ -88,9 +102,10 @@ struct sg_nvme_passthru_cmd
uint32_t cdw13;
uint32_t cdw14;
uint32_t cdw15;
-
+#ifdef SG_LIB_LINUX
uint32_t timeout_ms;
- uint32_t result;
+ uint32_t result; /* Dword(0) of completion queue entry */
+#endif
}
#ifdef SG_LIB_FREEBSD
__packed;
@@ -98,6 +113,7 @@ __packed;
;
#endif
+
/* Using byte offsets and unaligned be/le copies safer than packed
* structures. These are for sg_nvme_passthru_cmd . */
#define SG_NVME_PT_OPCODE 0
@@ -117,12 +133,46 @@ __packed;
#define SG_NVME_PT_CDW14 56
#define SG_NVME_PT_CDW15 60
+#ifdef SG_LIB_LINUX
+/* General references state that "all NVMe commands are 64 bytes long". If
+ * so then the following are add-ons by Linux, go to the OS and not the
+ * the NVMe device. And Linux doesn't seem to use the TIMEOUT_MS field on
+ * output to yield the "time taken" by the command. */
#define SG_NVME_PT_TIMEOUT_MS 64
#define SG_NVME_PT_RESULT 68
+#endif
+
+
+#ifdef __GNUC__
+#ifndef __clang__
+ struct __attribute__((__packed__)) sg_nvme_passthru_result
+#else
+ struct sg_nvme_passthru_result
+#endif
+#else
+struct sg_nvme_passthru_result
+#endif
+{
+ uint8_t status;
+ uint16_t transferred;
+ uint8_t reserved;
+}
+#ifdef SG_LIB_FREEBSD
+__packed;
+#else
+;
+#endif
+
+/* Using byte offsets and unaligned be/le copies safer than packed
+ * structures. These are for sg_nvme_passthru_result . */
+#define SG_NVME_PT_RES_STATUS 0
+#define SG_NVME_PT_RES_TRANSFERRED 1
+#define SG_NVME_PT_RES_RESERVED 3
+
/* Valid namespace IDs (nsid_s) range from 1 to 0xfffffffe, leaving: */
#define SG_NVME_BROADCAST_NSID 0xffffffff
-#define SG_NVME_CTL_NSID 0x0
+#define SG_NVME_CTL_NSID 0x0 /* the "controller's" namespace */
#ifdef __cplusplus
}
diff --git a/include/sg_unaligned.h b/include/sg_unaligned.h
index e40e359b..6a7b10e8 100644
--- a/include/sg_unaligned.h
+++ b/include/sg_unaligned.h
@@ -2,7 +2,7 @@
#define SG_UNALIGNED_H
/*
- * Copyright (c) 2014-2016 Douglas Gilbert.
+ * Copyright (c) 2014-2017 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -172,7 +172,7 @@ static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
/* Below are the little endian equivalents of the big endian functions
- * above. Little endian is used by ATA, networking and PCI.
+ * above. Little endian is used by ATA, PCI and NVMe.
*/
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
diff --git a/lib/sg_cmds_basic2.c b/lib/sg_cmds_basic2.c
index 4aaa7d88..1e352312 100644
--- a/lib/sg_cmds_basic2.c
+++ b/lib/sg_cmds_basic2.c
@@ -774,7 +774,7 @@ sg_ll_log_sense(int sg_fd, bool ppc, bool sp, int pc, int pg_code,
* various SG_LIB_CAT_* positive values or -1 -> other errors.
* Adds the ability to set the command abort timeout
* and the ability to report the residual count. If timeout_secs is zero
- * or less the the default command abort timeout (60 seconds) is used.
+ * or less the default command abort timeout (60 seconds) is used.
* If residp is non-NULL then the residual value is written where residp
* points. A residual value of 0 implies mx_resp_len bytes have be written
* where resp points. If the residual value equals mx_resp_len then no
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index ef06c4ff..003f1547 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -3039,6 +3039,11 @@ pr2serr(const char * fmt, ...)
#ifdef SG_LIB_FREEBSD
#include <sys/param.h>
+#elif defined(SG_LIB_WIN32)
+#include <windows.h>
+
+static bool got_page_size = false;
+static uint32_t win_page_size;
#endif
uint32_t
@@ -3047,7 +3052,14 @@ sg_get_page_size(void)
#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
return sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
#elif defined(SG_LIB_WIN32)
- return win_pagesize();
+ if (! got_page_size) {
+ SYSTEM_INFO si;
+
+ GetSystemInfo(&si);
+ win_page_size = si.dwPageSize;
+ got_page_size = true;
+ }
+ return win_page_size;
#elif defined(SG_LIB_FREEBSD)
return PAGE_SIZE;
#else
diff --git a/lib/sg_lib_data.c b/lib/sg_lib_data.c
index ce2b9f07..76964d8c 100644
--- a/lib/sg_lib_data.c
+++ b/lib/sg_lib_data.c
@@ -17,7 +17,7 @@
#endif
-const char * sg_lib_version_str = "2.33 20171205";/* spc5r17, sbc4r15 */
+const char * sg_lib_version_str = "2.34 20171219";/* spc5r17, sbc4r15 */
/* indexed by pdt; those that map to own index do not decay */
diff --git a/lib/sg_pt_freebsd.c b/lib/sg_pt_freebsd.c
index 08716bb8..6fa3f310 100644
--- a/lib/sg_pt_freebsd.c
+++ b/lib/sg_pt_freebsd.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_freebsd version 1.18 20171114 */
+/* sg_pt_freebsd version 1.19 20171218 */
#include <stdio.h>
#include <stdlib.h>
@@ -31,6 +31,8 @@
#include "sg_pt.h"
#include "sg_lib.h"
+#include "sg_unaligned.h"
+#include "sg_pt_nvme.h"
#include "freebsd_nvme_ioctl.h"
#ifdef HAVE_CONFIG_H
@@ -76,6 +78,7 @@ struct sg_pt_freebsd_scsi {
uint32_t dxfer_olen;
uint32_t mdxfer_len;
bool mdxfer_out;
+ bool scsi_dsense;
int scsi_status;
int resid;
int sense_resid;
@@ -112,6 +115,42 @@ pr2ws(const char * fmt, ...)
return n;
}
+static inline bool is_aligned(const void * pointer, size_t byte_count)
+{
+ return (sg_uintptr_t)pointer % byte_count == 0;
+}
+
+/* The web claims that all NVMe commands are 64 bytes long. Believe it until
+ * contradicted. The only SCSI commands that can be longer than 16 bytes are
+ * the Variable Length Commands (opcode 0x7f) and the XCDB wrapped commands
+ * (opcode 0x7e). Both have an inbuilt length field which can be cross
+ * checked with clen. */
+static bool
+is_scsi_command(const uint8_t * cdbp, int clen)
+{
+ int ilen, sa;
+
+ if (clen <= 16)
+ return true;
+ if (0 == (clen % 4)) {
+ if (0x7f == cdbp[0]) {
+ ilen = 8 + cdbp[7];
+ sa = sg_get_unaligned_be16(cdbp + 8);
+ if ((ilen == clen) && sa)
+ return true;
+ } else if (0x7e == cdbp[0]) {
+ ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
+ if (ilen == clen)
+ return true;
+ }
+ }
+ if ((clen >= 64) && (clen <= 72))
+ return false;
+ pr2ws("%s: irregular command, assume NVMe:\n", __func__);
+ dStrHexErr((const char *)cdbp, clen, 1);
+ return false;
+}
+
/* Returns >= 0 if successful. If error in Unix returns negated errno. */
int
@@ -124,7 +163,7 @@ scsi_pt_open_device(const char * device_name, bool read_only, int verbose)
}
/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
- * together. The 'flags' is only used on NVMe devices. It is ignored on
+ * together. The 'oflags' is only used on NVMe devices. It is ignored on
* SCSI and ATA devices in FreeBSD.
* Returns >= 0 if successful, otherwise returns negated errno. */
int
@@ -859,7 +898,7 @@ get_pt_nvme_nsid(const struct sg_pt_base * vp)
fdchan = devicetable[han];
if (NULL == fdchan)
return 0;
- return fdchan->nsid ;
+ return fdchan->nsid;
}
return 0;
}
@@ -876,3 +915,683 @@ get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
b[max_b_len - 1] = '\0';
return b;
}
+
+
+#define SCSI_INQUIRY_OPC 0x12
+#define SCSI_REPORT_LUNS_OPC 0xa0
+#define SCSI_TEST_UNIT_READY_OPC 0x0
+#define SCSI_REQUEST_SENSE_OPC 0x3
+#define SCSI_SEND_DIAGNOSTIC_OPC 0x1d
+#define SCSI_RECEIVE_DIAGNOSTIC_OPC 0x1c
+
+/* Additional Sense Code (ASC) */
+#define NO_ADDITIONAL_SENSE 0x0
+#define LOGICAL_UNIT_NOT_READY 0x4
+#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8
+#define UNRECOVERED_READ_ERR 0x11
+#define PARAMETER_LIST_LENGTH_ERR 0x1a
+#define INVALID_OPCODE 0x20
+#define LBA_OUT_OF_RANGE 0x21
+#define INVALID_FIELD_IN_CDB 0x24
+#define INVALID_FIELD_IN_PARAM_LIST 0x26
+#define UA_RESET_ASC 0x29
+#define UA_CHANGED_ASC 0x2a
+#define TARGET_CHANGED_ASC 0x3f
+#define LUNS_CHANGED_ASCQ 0x0e
+#define INSUFF_RES_ASC 0x55
+#define INSUFF_RES_ASCQ 0x3
+#define LOW_POWER_COND_ON_ASC 0x5e /* ASCQ=0 */
+#define POWER_ON_RESET_ASCQ 0x0
+#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */
+#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */
+#define CAPACITY_CHANGED_ASCQ 0x9
+#define SAVING_PARAMS_UNSUP 0x39
+#define TRANSPORT_PROBLEM 0x4b
+#define THRESHOLD_EXCEEDED 0x5d
+#define LOW_POWER_COND_ON 0x5e
+#define MISCOMPARE_VERIFY_ASC 0x1d
+#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
+#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
+
+
+static void
+build_sense_buffer(bool desc, uint8_t *buf, uint8_t skey, uint8_t asc,
+ uint8_t ascq)
+{
+ if (desc) {
+ buf[0] = 0x72; /* descriptor, current */
+ buf[1] = skey;
+ buf[2] = asc;
+ buf[3] = ascq;
+ buf[7] = 0;
+ } else {
+ buf[0] = 0x70; /* fixed, current */
+ buf[2] = skey;
+ buf[7] = 0xa; /* Assumes length is 18 bytes */
+ buf[12] = asc;
+ buf[13] = ascq;
+ }
+}
+
+/* Set in_bit to -1 to indicate no bit position of invalid field */
+static void
+mk_sense_asc_ascq(struct sg_pt_freebsd_scsi * ptp, int sk, int asc, int ascq,
+ int vb)
+{
+ bool dsense = ptp->scsi_dsense;
+ int n;
+ uint8_t * sbp = ptp->sense;
+
+ ptp->scsi_status = SAM_STAT_CHECK_CONDITION;
+ n = ptp->sense_len;
+ if ((n < 8) || ((! dsense) && (n < 14))) {
+ pr2ws("%s: sense_len=%d too short, want 14 or more\n", __func__, n);
+ return;
+ } else
+ ptp->sense_resid = ptp->sense_len -
+ (dsense ? 8 : ((n < 18) ? n : 18));
+ memset(sbp, 0, n);
+ build_sense_buffer(dsense, sbp, sk, asc, ascq);
+ if (vb > 3)
+ pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x%x]\n", __func__, asc,
+ ascq);
+}
+
+/* Set in_bit to -1 to indicate no bit position of invalid field */
+static void
+mk_sense_invalid_fld(struct sg_pt_freebsd_scsi * ptp, bool in_cdb,
+ int in_byte, int in_bit, int vb)
+{
+ bool ds = ptp->scsi_dsense;
+ int sl, asc, n;
+ uint8_t * sbp = (uint8_t *)ptp->sense;
+ uint8_t sks[4];
+
+ ptp->scsi_status = SAM_STAT_CHECK_CONDITION;
+ asc = in_cdb ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST;
+ n = ptp->sense_len;
+ if ((n < 8) || ((! ds) && (n < 14))) {
+ pr2ws("%s: max_response_len=%d too short, want 14 or more\n",
+ __func__, n);
+ return;
+ } else
+ ptp->sense_resid = ptp->sense_len - (ds ? 8 : ((n < 18) ? n : 18));
+ memset(sbp, 0, n);
+ build_sense_buffer(ds, sbp, SPC_SK_ILLEGAL_REQUEST, asc, 0);
+ memset(sks, 0, sizeof(sks));
+ sks[0] = 0x80;
+ if (in_cdb)
+ sks[0] |= 0x40;
+ if (in_bit >= 0) {
+ sks[0] |= 0x8;
+ sks[0] |= (0x7 & in_bit);
+ }
+ sg_put_unaligned_be16(in_byte, sks + 1);
+ if (ds) {
+ sl = sbp[7] + 8;
+ sbp[7] = sl;
+ sbp[sl] = 0x2;
+ sbp[sl + 1] = 0x6;
+ memcpy(sbp + sl + 4, sks, 3);
+ } else
+ memcpy(sbp + 15, sks, 3);
+ if (vb > 3)
+ pr2ws("%s: [sense_key,asc,ascq]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n",
+ __func__, asc, in_cdb ? 'C' : 'D', in_byte, in_bit);
+}
+
+#if 0
+static int
+do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
+ struct sg_nvme_passthru_cmd *cmdp, int time_secs,
+ bool cp_cmd_out2resp, int vb)
+{
+ const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
+ uint32_t n;
+
+ cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
+ if (vb > 2) {
+ pr2ws("NVMe command:\n");
+ dStrHex((const char *)cmdp, cmd_len, 1);
+ }
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp) < 0) {
+ ptp->os_err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
+ __func__, strerror(ptp->os_err), ptp->os_err);
+ return -ptp->os_err;
+ } else
+ ptp->os_err = 0;
+ ptp->nvme_result = cmdp->result;
+ if (cp_cmd_out2resp) {
+ n = ptp->io_hdr.max_response_len;
+ if ((n > 0) && ptp->io_hdr.response) {
+ n = (n < cmd_len) ? n : cmd_len;
+ memcpy((uint8_t *)ptp->io_hdr.response, cmdp, n);
+ ptp->io_hdr.response_len = n;
+ } else
+ ptp->io_hdr.response_len = 0;
+ } else
+ ptp->io_hdr.response_len = 0;
+
+ if (vb > 2)
+ pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmdp->timeout_ms,
+ cmdp->result);
+ return 0;
+}
+#endif
+
+static int
+sntl_cache_identity(struct sg_pt_freebsd_scsi * ptp, int time_secs, int vb)
+{
+ int err;
+ struct sg_nvme_passthru_cmd cmd;
+ uint32_t pg_sz = sg_get_page_size();
+
+ ptp->nvme_id_ctlp = sg_memalign(pg_sz, pg_sz, &ptp->free_nvme_id_ctlp,
+ vb > 3);
+ if (NULL == ptp->nvme_id_ctlp) {
+ pr2ws("%s: sg_memalign() failed to get memory\n", __func__);
+ return SG_LIB_OS_BASE_ERR + ENOMEM;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0x6; /* Identify */
+ cmd.cdw10 = 0x1; /* CNS=0x1 Identify controller */
+ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
+ cmd.data_len = pg_sz;
+ cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)"
+ "\n", __func__, strerror(err), err);
+ ptp->os_err = err;
+ return -err;
+ }
+ return 0;
+}
+
+static const char * nvme_scsi_vendor_str = "NVMe ";
+static const uint16_t inq_resp_len = 36;
+
+static int
+sntl_inq(struct sg_pt_freebsd_scsi * ptp, const uint8_t * cdbp, int time_secs,
+ int vb)
+{
+ bool evpd;
+ int res;
+ uint16_t k, n, alloc_len, pg_cd;
+ uint8_t inq_dout[128];
+
+ if (vb > 3)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+
+ if (0x2 & cdbp[1]) {
+ mk_sense_invalid_fld(ptp, true, 1, 1, vb);
+ return 0;
+ }
+ if (NULL == ptp->nvme_id_ctlp) {
+ res = sntl_cache_identity(ptp, time_secs, vb);
+ if (res)
+ return res;
+ }
+ memset(inq_dout, 0, sizeof(inq_dout));
+ alloc_len = sg_get_unaligned_be16(cdbp + 3);
+ evpd = !!(0x1 & cdbp[1]);
+ pg_cd = cdbp[2];
+ if (evpd) { /* VPD page responses */
+ switch (pg_cd) {
+ case 0:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ inq_dout[1] = pg_cd;
+ sg_put_unaligned_be16(3, inq_dout + 2);
+ inq_dout[4] = 0x0;
+ inq_dout[5] = 0x80;
+ inq_dout[6] = 0x83;
+ n = 7;
+ break;
+ case 0x80:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ inq_dout[1] = pg_cd;
+ sg_put_unaligned_be16(20, inq_dout + 2);
+ memcpy(inq_dout + 4, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ n = 24;
+ break;
+ case 0x83:
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); prefer pdt=0xd --> SES */
+ inq_dout[1] = pg_cd;
+ inq_dout[4] = 0x2; /* Prococol id=0, code_set=2 (ASCII) */
+ inq_dout[5] = 0x1; /* PIV=0, ASSOC=0 (LU ??), desig_id=1 */
+ /* Building T10 Vendor ID base designator, SNTL document 1.5
+ * dated 20150624 confuses this with SCSI name string
+ * descriptor, desig_id=8 */
+ memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8);
+ memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 40); /* MN */
+ for (k = 40; k > 0; --k) {
+ if (' ' == inq_dout[16 + k - 1])
+ inq_dout[16 + k - 1] = '_'; /* convert trailing spaces */
+ else
+ break;
+ }
+ memcpy(inq_dout + 16 + k + 1, ptp->nvme_id_ctlp + 4, 20); /* SN */
+ n = 16 + k + 1 + 20;
+ inq_dout[7] = 8 + k + 1 + 20;
+ sg_put_unaligned_be16(n - 4, inq_dout + 2);
+ break;
+ default: /* Point to page_code field in cdb */
+ mk_sense_invalid_fld(ptp, true, 2, 7, vb);
+ return 0;
+ }
+ if (alloc_len > 0) {
+ n = (alloc_len < n) ? alloc_len : n;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0)
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ }
+ } else { /* Standard INQUIRY response */
+ /* inq_dout[0] = (PQ=0)<<5 | (PDT=0); pdt=0 --> SBC; 0xd --> SES */
+ inq_dout[2] = 6; /* version: SPC-4 */
+ inq_dout[3] = 2; /* NORMACA=0, HISUP=0, response data format: 2 */
+ inq_dout[4] = 31; /* so response length is (or could be) 36 bytes */
+ inq_dout[6] = 0x40; /* ENCSERV=1 */
+ inq_dout[7] = 0x2; /* CMDQUE=1 */
+ memcpy(inq_dout + 8, nvme_scsi_vendor_str, 8); /* NVMe not Intel */
+ memcpy(inq_dout + 16, ptp->nvme_id_ctlp + 24, 16); /* Prod <-- MN */
+ memcpy(inq_dout + 32, ptp->nvme_id_ctlp + 64, 4); /* Rev <-- FR */
+ if (alloc_len > 0) {
+ n = (alloc_len < inq_resp_len) ? alloc_len : inq_resp_len;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0)
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, inq_dout, n);
+ }
+ }
+ return 0;
+}
+
+static int
+sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
+ int vb)
+{
+ int res;
+ uint16_t sel_report;
+ uint32_t alloc_len, k, n, num, max_nsid;
+ uint8_t * rl_doutp;
+ uint8_t * up;
+
+ if (vb > 3)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+
+ sel_report = cdbp[2];
+ alloc_len = sg_get_unaligned_be32(cdbp + 6);
+ if (NULL == ptp->nvme_id_ctlp) {
+ res = sntl_cache_identity(ptp, time_secs, vb);
+ if (res)
+ return res;
+ }
+ max_nsid = sg_get_unaligned_le32(ptp->nvme_id_ctlp + 516);
+ switch (sel_report) {
+ case 0:
+ case 2:
+ num = max_nsid;
+ break;
+ case 1:
+ case 0x10:
+ case 0x12:
+ num = 0;
+ break;
+ case 0x11:
+ num = (1 == ptp->nvme_nsid) ? max_nsid : 0;
+ break;
+ default:
+ if (vb > 1)
+ pr2ws("%s: bad select_report value: 0x%x\n", __func__,
+ sel_report);
+ mk_sense_invalid_fld(ptp, true, 2, 7, vb);
+ return 0;
+ }
+ rl_doutp = (uint8_t *)calloc(num + 1, 8);
+ if (NULL == rl_doutp) {
+ pr2ws("%s: calloc() failed to get memory\n", __func__);
+ return SG_LIB_OS_BASE_ERR + ENOMEM;
+ }
+ for (k = 0, up = rl_doutp + 8; k < num; ++k, up += 8)
+ sg_put_unaligned_be16(k, up);
+ n = num * 8;
+ sg_put_unaligned_be32(n, rl_doutp);
+ n+= 8;
+ if (alloc_len > 0) {
+ n = (alloc_len < n) ? alloc_len : n;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0) {
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, rl_doutp, n);
+ ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ }
+ }
+ res = 0;
+ free(rl_doutp);
+ return res;
+}
+
+static int
+sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
+{
+ int res, err;
+ uint32_t pow_state;
+ struct sg_nvme_passthru_cmd cmd;
+
+ if (vb > 3)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+ if (NULL == ptp->nvme_id_ctlp) {
+ res = sntl_cache_identity(ptp, time_secs, vb);
+ if (res)
+ return res;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0xa; /* Get feature */
+ cmd.nsid = SG_NVME_BROADCAST_NSID;
+ cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
+ cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
+ "\n", __func__, strerror(err), err);
+ ptp->os_err = err;
+ return -err;
+ }
+ pow_state = (0x1f & cmd.result);
+ if (vb > 3)
+ pr2ws("%s: pow_state=%u\n", __func__, pow_state);
+#if 0 /* pow_state bounces around too much on laptop */
+ if (pow_state)
+ mk_sense_asc_ascq(ptp, SPC_SK_NOT_READY, LOW_POWER_COND_ON_ASC, 0,
+ vb);
+#endif
+ return 0;
+}
+
+static int
+sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool desc;
+ int res, err;
+ uint32_t pow_state, alloc_len, n;
+ struct sg_nvme_passthru_cmd cmd;
+ uint8_t rs_dout[64];
+
+ if (vb > 3)
+ pr2ws("%s: time_secs=%d\n", __func__, time_secs);
+ if (NULL == ptp->nvme_id_ctlp) {
+ res = sntl_cache_identity(ptp, time_secs, vb);
+ if (res)
+ return res;
+ }
+ desc = !!(0x1 & cdbp[1]);
+ alloc_len = cdbp[4];
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0xa; /* Get feature */
+ cmd.nsid = SG_NVME_BROADCAST_NSID;
+ cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
+ cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
+ if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
+ err = errno;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
+ "\n", __func__, strerror(err), err);
+ ptp->os_err = err;
+ return -err;
+ }
+ pow_state = (0x1f & cmd.result);
+ if (vb > 3)
+ pr2ws("%s: pow_state=%u\n", __func__, pow_state);
+ memset(rs_dout, 0, sizeof(rs_dout));
+ if (pow_state)
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ LOW_POWER_COND_ON_ASC, 0);
+ else
+ build_sense_buffer(desc, rs_dout, SPC_SK_NO_SENSE,
+ NO_ADDITIONAL_SENSE, 0);
+ n = desc ? 8 : 18;
+ n = (n < alloc_len) ? n : alloc_len;
+ n = (n < ptp->io_hdr.din_xfer_len) ? n : ptp->io_hdr.din_xfer_len;
+ if (n > 0) {
+ memcpy((uint8_t *)ptp->io_hdr.din_xferp, rs_dout, n);
+ ptp->io_hdr.din_resid = ptp->io_hdr.din_xfer_len - n;
+ }
+ return 0;
+}
+
+/* This is not really a SNTL. For SCSI SEND DIAGNOSTIC(PF=1) NVMe-MI
+ * has a special command (SES Send) to tunnel through pages to an
+ * enclosure. The NVMe enclosure is meant to understand the SES
+ * (SCSI Enclosure Services) use of diagnostics pages that are
+ * related to SES. */
+static int
+sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool pf, self_test;
+ uint8_t st_cd, dpg_cd;
+ uint32_t alloc_len, n, dout_len, dpg_len;
+ uint32_t pg_sz = sg_get_page_size();
+ const uint8_t * dop;
+ struct sg_nvme_passthru_cmd cmd;
+
+ st_cd = 0x7 & (cdbp[1] >> 5);
+ pf = !! (0x4 & cdbp[1]);
+ self_test = !! (0x10 & cdbp[1]);
+ if (vb > 3)
+ pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
+ (int)self_test, (int)st_cd);
+ if (self_test)
+ return 0; /* NVMe has no self-test, just say OK */
+ alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
+ dout_len = ptp->io_hdr.dout_xfer_len;
+ if (pf) {
+ if (0 == alloc_len) {
+ mk_sense_invalid_fld(ptp, true, 3, 7, vb);
+ if (vb)
+ pr2ws("%s: PF bit set bit param_list_len=0\n", __func__);
+ return 0;
+ }
+ } else { /* PF bit clear */
+ if (alloc_len) {
+ mk_sense_invalid_fld(ptp, true, 3, 7, vb);
+ if (vb)
+ pr2ws("%s: param_list_len>0 but PF clear\n", __func__);
+ return 0;
+ } else
+ return 0; /* nothing to do */
+ if (dout_len > 0) {
+ if (vb)
+ pr2ws("%s: dout given but PF clear\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ }
+ if (dout_len < 4) {
+ if (vb)
+ pr2ws("%s: dout length (%u bytes) too short\n", __func__,
+ dout_len);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ n = dout_len;
+ n = (n < alloc_len) ? n : alloc_len;
+ dop = (const uint8_t *)ptp->io_hdr.dout_xferp;
+ if (! is_aligned(dop, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
+ if (vb)
+ pr2ws("%s: dout [0x%" PRIx64 "] not page aligned\n", __func__,
+ (uint64_t)ptp->io_hdr.dout_xferp);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ dpg_cd = dop[0];
+ dpg_len = sg_get_unaligned_be16(dop + 2) + 4;
+ /* should we allow for more than one D_PG is dout ?? */
+ n = (n < dpg_len) ? n : dpg_len; /* not yet ... */
+
+ if (vb)
+ pr2ws("%s: passing through d_pg=0x%x, len=%u to NVME_MI SES send\n",
+ __func__, dpg_cd, dpg_len);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0x1d; /* MI send; hmmm same opcode as SEND DIAG */
+ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
+ /* dout_len > 0x1000, is this a problem?? */
+ cmd.cdw10 = 0x0804; /* NVMe Message Header */
+ cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
+ cmd.cdw13 = n;
+ return do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+}
+
+/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
+ * NVMe-MI has a special command (SES Receive) to read pages through a
+ * tunnel from an enclosure. The NVMe enclosure is meant to understand the
+ * SES (SCSI Enclosure Services) use of diagnostics pages that are
+ * related to SES. */
+static int
+sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
+ int time_secs, int vb)
+{
+ bool pcv;
+ int res;
+ uint8_t dpg_cd;
+ uint32_t alloc_len, n, din_len;
+ uint32_t pg_sz = sg_get_page_size();
+ const uint8_t * dip;
+ struct sg_nvme_passthru_cmd cmd;
+
+ pcv = !! (0x1 & cdbp[1]);
+ dpg_cd = cdbp[2];
+ alloc_len = sg_get_unaligned_be16(cdbp + 3); /* parameter list length */
+ if (vb > 3)
+ pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__,
+ dpg_cd, (int)pcv, alloc_len);
+ din_len = ptp->io_hdr.din_xfer_len;
+ if (pcv) {
+ if (0 == alloc_len) {
+ /* T10 says not an error, hmmm */
+ mk_sense_invalid_fld(ptp, true, 3, 7, vb);
+ if (vb)
+ pr2ws("%s: PCV bit set bit but alloc_len=0\n", __func__);
+ return 0;
+ }
+ } else { /* PCV bit clear */
+ if (alloc_len) {
+ mk_sense_invalid_fld(ptp, true, 3, 7, vb);
+ if (vb)
+ pr2ws("%s: alloc_len>0 but PCV clear\n", __func__);
+ return 0;
+ } else
+ return 0; /* nothing to do */
+ if (din_len > 0) {
+ if (vb)
+ pr2ws("%s: din given but PCV clear\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ }
+ n = din_len;
+ n = (n < alloc_len) ? n : alloc_len;
+ dip = (const uint8_t *)ptp->io_hdr.din_xferp;
+ if (! is_aligned(dip, pg_sz)) { /* caller best use sg_memalign(,pg_sz) */
+ if (vb)
+ pr2ws("%s: din [0x%" PRIx64 "] not page aligned\n", __func__,
+ (uint64_t)ptp->io_hdr.din_xferp);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+
+ if (vb)
+ pr2ws("%s: expecting d_pg=0x%x from NVME_MI SES receive\n", __func__,
+ dpg_cd);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0x1e; /* MI receive */
+ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ cmd.data_len = 0x1000; /* NVMe 4k page size. Maybe determine this? */
+ /* din_len > 0x1000, is this a problem?? */
+ cmd.cdw10 = 0x0804; /* NVMe Message Header */
+ cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
+ cmd.cdw12 = dpg_cd;
+ cmd.cdw13 = n;
+ res = do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+ ptp->io_hdr.din_resid = din_len - n;
+ return res;
+}
+
+/* Executes NVMe Admin command (or at least forwards it to lower layers).
+ * Returns 0 for success, negative numbers are negated 'errno' values from
+ * OS system calls. Positive return values are errors from this package.
+ * When time_secs is 0 the Linux NVMe Admin command default of 60 seconds
+ * is used. */
+int
+sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
+{
+ bool scsi_cmd;
+ int n, len;
+ struct sg_pt_linux_scsi * ptp = &vp->impl;
+ struct sg_nvme_passthru_cmd cmd;
+ const uint8_t * cdbp;
+
+ if (! ptp->io_hdr.request) {
+ if (vb)
+ pr2ws("No NVMe command given (set_scsi_pt_cdb())\n");
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ if (fd >= 0) {
+ if ((ptp->dev_fd >= 0) && (fd != ptp->dev_fd)) {
+ if (vb)
+ pr2ws("%s: file descriptor given to create() and here "
+ "differ\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ ptp->dev_fd = fd;
+ } else if (ptp->dev_fd < 0) {
+ if (vb)
+ pr2ws("%s: invalid file descriptors\n", __func__);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ n = ptp->io_hdr.request_len;
+ cdbp = (const uint8_t *)ptp->io_hdr.request;
+ if (vb > 3)
+ pr2ws("%s: opcode=0x%x, fd=%d, time_secs=%d\n", __func__, cdbp[0],
+ fd, time_secs);
+ scsi_cmd = is_scsi_command(cdbp, n);
+ if (scsi_cmd) {
+ switch (cdbp[0]) {
+ case SCSI_INQUIRY_OPC:
+ return sntl_inq(ptp, cdbp, time_secs, vb);
+ case SCSI_REPORT_LUNS_OPC:
+ return sntl_rluns(ptp, cdbp, time_secs, vb);
+ case SCSI_TEST_UNIT_READY_OPC:
+ return sntl_tur(ptp, time_secs, vb);
+ case SCSI_REQUEST_SENSE_OPC:
+ return sntl_req_sense(ptp, cdbp, time_secs, vb);
+ case SCSI_SEND_DIAGNOSTIC_OPC:
+ return sntl_senddiag(ptp, cdbp, time_secs, vb);
+ case SCSI_RECEIVE_DIAGNOSTIC_OPC:
+ return sntl_recvdiag(ptp, cdbp, time_secs, vb);
+// xxxxxxxxxx
+ default:
+ mk_sense_asc_ascq(ptp, SPC_SK_ILLEGAL_REQUEST, INVALID_OPCODE,
+ 0, vb);
+ return 0;
+ }
+ }
+ len = (int)sizeof(cmd);
+ n = (n < len) ? n : len;
+ if (n < 64) {
+ if (vb)
+ pr2ws("%s: command length of %d bytes is too short\n", __func__,
+ n);
+ return SCSI_PT_DO_BAD_PARAMS;
+ }
+ memcpy(&cmd, (const uint8_t *)ptp->io_hdr.request, n);
+ if (n < len) /* zero out rest of 'cmd' */
+ memset((unsigned char *)&cmd + n, 0, len - n);
+ if (ptp->io_hdr.din_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.din_xfer_len;
+ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.din_xferp;
+ } else if (ptp->io_hdr.dout_xfer_len > 0) {
+ cmd.data_len = ptp->io_hdr.dout_xfer_len;
+ cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
+ }
+ return do_nvme_admin_cmd(ptp, &cmd, time_secs, true, vb);
+}
diff --git a/lib/sg_pt_linux.c b/lib/sg_pt_linux.c
index 0b057568..91c4c250 100644
--- a/lib/sg_pt_linux.c
+++ b/lib/sg_pt_linux.c
@@ -660,7 +660,7 @@ get_scsi_pt_status_response(const struct sg_pt_base * vp)
if (NULL == ptp)
return 0;
- return (int)(ptp->is_nvme ? ptp->nvme_result :
+ return (int)(ptp->is_nvme ? ptp->nvme_status :
ptp->io_hdr.device_status);
return ptp->io_hdr.device_status;
}
@@ -672,7 +672,7 @@ get_pt_result(const struct sg_pt_base * vp)
if (NULL == ptp)
return 0;
- return ptp->is_nvme ? ptp->nvme_result :
+ return ptp->is_nvme ? ptp->nvme_status :
ptp->io_hdr.device_status;
}
diff --git a/lib/sg_pt_linux_nvme.c b/lib/sg_pt_linux_nvme.c
index 769e84e8..c18a854a 100644
--- a/lib/sg_pt_linux_nvme.c
+++ b/lib/sg_pt_linux_nvme.c
@@ -8,7 +8,38 @@
* was provided by WDC in November 2017.
*/
-/* sg_pt_linux_nvme version 1.00 20171207 */
+/*
+ * Copyright 2017, Western Digital Corporation
+ *
+ * Written by Berck Nash
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * Based on the NVM-Express command line utility, which bore the following
+ * notice:
+ *
+ * Copyright (c) 2014-2015, Intel Corporation.
+ *
+ * Written by Keith Busch <keith.busch@intel.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
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+/* sg_pt_linux_nvme version 1.03 20171219 */
#include <stdio.h>
@@ -224,47 +255,45 @@ mk_sense_invalid_fld(struct sg_pt_linux_scsi * ptp, bool in_cdb, int in_byte,
static int
do_nvme_admin_cmd(struct sg_pt_linux_scsi * ptp,
- struct sg_nvme_passthru_cmd *cmdp, int time_secs,
- bool cp_cmd_out2resp, int vb)
+ struct sg_nvme_passthru_cmd *cmdp, int time_secs, int vb)
{
const uint32_t cmd_len = sizeof(struct sg_nvme_passthru_cmd);
- uint32_t n;
+ int res;
cmdp->timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
if (vb > 2) {
pr2ws("NVMe command:\n");
dStrHex((const char *)cmdp, cmd_len, 1);
}
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp) < 0) {
- ptp->os_err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)\n",
- __func__, strerror(ptp->os_err), ptp->os_err);
- return -ptp->os_err;
- } else
+ res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, cmdp);
+ if (0 != res) {
+ if (res < 0) { /* OS error (errno negated) */
+ ptp->os_err = -res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN_CMD) failed: %s "
+ "(errno=%d)\n", __func__, strerror(ptp->os_err),
+ ptp->os_err);
+ return -res;
+ } else { /* NVMe errors are positive return values */
+ ptp->nvme_status = res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN_CMD) failed: NVMe status "
+ "(SF) 0x%x\n", __func__, res);
+ return SG_LIB_NVME_STATUS;
+ }
+ } else {
ptp->os_err = 0;
+ ptp->nvme_status = 0;
+ }
ptp->nvme_result = cmdp->result;
- if (cp_cmd_out2resp) {
- n = ptp->io_hdr.max_response_len;
- if ((n > 0) && ptp->io_hdr.response) {
- n = (n < cmd_len) ? n : cmd_len;
- memcpy((uint8_t *)ptp->io_hdr.response, cmdp, n);
- ptp->io_hdr.response_len = n;
- } else
- ptp->io_hdr.response_len = 0;
- } else
- ptp->io_hdr.response_len = 0;
-
- if (vb > 2)
- pr2ws("%s: timeout_ms=%u, result=%u\n", __func__, cmdp->timeout_ms,
- cmdp->result);
+ ptp->io_hdr.response_len = 0;
return 0;
}
static int
sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
{
- int err;
+ int res;
struct sg_nvme_passthru_cmd cmd;
uint32_t pg_sz = sg_get_page_size();
@@ -280,14 +309,28 @@ sntl_cache_identity(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->nvme_id_ctlp;
cmd.data_len = pg_sz;
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (0 != res) {
+ if (res < 0) { /* OS error (errno negated) */
+ ptp->os_err = -res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN_CMD(Identify)) failed: %s "
+ "(errno=%d)\n", __func__, strerror(ptp->os_err),
+ ptp->os_err);
+ return -res;
+ } else { /* NVMe errors are positive return values */
+ ptp->nvme_status = res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(NVME_IOCTL_ADMIN_CMD) failed: NVMe status "
+ "(SF) 0x%x\n", __func__, res);
+ return SG_LIB_NVME_STATUS;
+ }
+ } else {
+ ptp->os_err = 0;
+ ptp->nvme_status = 0;
}
+ ptp->nvme_result = cmd.result;
+ ptp->io_hdr.response_len = 0;
return 0;
}
@@ -455,7 +498,7 @@ sntl_rluns(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp, int time_secs,
static int
sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
{
- int res, err;
+ int res;
uint32_t pow_state;
struct sg_nvme_passthru_cmd cmd;
@@ -471,15 +514,29 @@ sntl_tur(struct sg_pt_linux_scsi * ptp, int time_secs, int vb)
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (0 != res) {
+ if (res < 0) { /* OS error (errno negated) */
+ ptp->os_err = -res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: %s "
+ "(errno=%d)\n", __func__, strerror(ptp->os_err),
+ ptp->os_err);
+ return -res;
+ } else { /* NVMe errors are positive return values */
+ ptp->nvme_status = res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN_CMD(Get feature)) failed: NVMe "
+ "status (SF) 0x%x\n", __func__, res);
+ return SG_LIB_NVME_STATUS;
+ }
+ } else {
+ ptp->os_err = 0;
+ ptp->nvme_status = 0;
}
- pow_state = (0x1f & cmd.result);
+ ptp->nvme_result = cmd.result;
+ ptp->io_hdr.response_len = 0;
+ pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
#if 0 /* pow_state bounces around too much on laptop */
@@ -495,7 +552,7 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
int time_secs, int vb)
{
bool desc;
- int res, err;
+ int res;
uint32_t pow_state, alloc_len, n;
struct sg_nvme_passthru_cmd cmd;
uint8_t rs_dout[64];
@@ -514,15 +571,29 @@ sntl_req_sense(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
cmd.nsid = SG_NVME_BROADCAST_NSID;
cmd.cdw10 = 0x2; /* SEL=0 (current), Feature=2 Power Management */
cmd.timeout_ms = (time_secs < 0) ? 0 : (1000 * time_secs);
- if (ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd) < 0) {
- err = errno;
- if (vb > 2)
- pr2ws("%s: ioctl(NVME_ADMIN(Get feature)) failed: %s (errno=%d)"
- "\n", __func__, strerror(err), err);
- ptp->os_err = err;
- return -err;
+ res = ioctl(ptp->dev_fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (0 != res) {
+ if (res < 0) { /* OS error (errno negated) */
+ ptp->os_err = -res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN(Get feature)) failed: %s "
+ "(errno=%d)\n", __func__, strerror(ptp->os_err),
+ ptp->os_err);
+ return -res;
+ } else { /* NVMe errors are positive return values */
+ ptp->nvme_status = res;
+ if (vb > 2)
+ pr2ws("%s: ioctl(ADMIN(Get feature)) failed: NVMe "
+ "status (SF) 0x%x\n", __func__, res);
+ return SG_LIB_NVME_STATUS;
+ }
+ } else {
+ ptp->os_err = 0;
+ ptp->nvme_status = 0;
}
- pow_state = (0x1f & cmd.result);
+ ptp->nvme_result = cmd.result;
+ ptp->io_hdr.response_len = 0;
+ pow_state = (0x1f & ptp->nvme_result);
if (vb > 3)
pr2ws("%s: pow_state=%u\n", __func__, pow_state);
memset(rs_dout, 0, sizeof(rs_dout));
@@ -559,8 +630,8 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
struct sg_nvme_passthru_cmd cmd;
st_cd = 0x7 & (cdbp[1] >> 5);
- pf = !! (0x4 & cdbp[1]);
- self_test = !! (0x10 & cdbp[1]);
+ self_test = !! (0x4 & cdbp[1]);
+ pf = !! (0x10 & cdbp[1]);
if (vb > 3)
pr2ws("%s: pf=%d, self_test=%d (st_code=%d)\n", __func__, (int)pf,
(int)self_test, (int)st_cd);
@@ -620,7 +691,7 @@ sntl_senddiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
cmd.cdw10 = 0x0804; /* NVMe Message Header */
cmd.cdw11 = 0x9; /* nvme_mi_ses_send; (0x8 -> mi_ses_recv) */
cmd.cdw13 = n;
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+ return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
}
/* This is not really a SNTL. For SCSI RECEIVE DIAGNOSTIC RESULTS(PCV=1)
@@ -647,28 +718,6 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
pr2ws("%s: dpg_cd=0x%x, pcv=%d, alloc_len=0x%x\n", __func__,
dpg_cd, (int)pcv, alloc_len);
din_len = ptp->io_hdr.din_xfer_len;
- if (pcv) {
- if (0 == alloc_len) {
- /* T10 says not an error, hmmm */
- mk_sense_invalid_fld(ptp, true, 3, 7, vb);
- if (vb)
- pr2ws("%s: PCV bit set bit but alloc_len=0\n", __func__);
- return 0;
- }
- } else { /* PCV bit clear */
- if (alloc_len) {
- mk_sense_invalid_fld(ptp, true, 3, 7, vb);
- if (vb)
- pr2ws("%s: alloc_len>0 but PCV clear\n", __func__);
- return 0;
- } else
- return 0; /* nothing to do */
- if (din_len > 0) {
- if (vb)
- pr2ws("%s: din given but PCV clear\n", __func__);
- return SCSI_PT_DO_BAD_PARAMS;
- }
- }
n = din_len;
n = (n < alloc_len) ? n : alloc_len;
dip = (const uint8_t *)ptp->io_hdr.din_xferp;
@@ -691,7 +740,7 @@ sntl_recvdiag(struct sg_pt_linux_scsi * ptp, const uint8_t * cdbp,
cmd.cdw11 = 0x8; /* nvme_mi_ses_receive */
cmd.cdw12 = dpg_cd;
cmd.cdw13 = n;
- res = do_nvme_admin_cmd(ptp, &cmd, time_secs, false, vb);
+ res = do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
ptp->io_hdr.din_resid = din_len - n;
return res;
}
@@ -773,5 +822,5 @@ sg_do_nvme_pt(struct sg_pt_base * vp, int fd, int time_secs, int vb)
cmd.data_len = ptp->io_hdr.dout_xfer_len;
cmd.addr = (uint64_t)(sg_uintptr_t)ptp->io_hdr.dout_xferp;
}
- return do_nvme_admin_cmd(ptp, &cmd, time_secs, true, vb);
+ return do_nvme_admin_cmd(ptp, &cmd, time_secs, vb);
}
diff --git a/lib/sg_pt_win32.c b/lib/sg_pt_win32.c
index e64614c7..af018244 100644
--- a/lib/sg_pt_win32.c
+++ b/lib/sg_pt_win32.c
@@ -5,7 +5,7 @@
* license that can be found in the BSD_LICENSE file.
*/
-/* sg_pt_win32 version 1.18 20171108 */
+/* sg_pt_win32 version 1.19 20171209 */
#include <stdio.h>
#include <stdlib.h>
@@ -59,7 +59,7 @@
#define WIN32_FDOFFSET 32
struct sg_pt_handle {
- int in_use;
+ bool in_use;
HANDLE fh;
char adapter[32];
int bus;
@@ -71,9 +71,10 @@ struct sg_pt_handle {
static struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT];
struct sg_pt_win32_scsi {
- unsigned char * dxferp;
+ bool is_nvme;
+ bool mdxfer_out; /* direction of metadata xfer, true->data-out */
+ bool scsi_dsense; /* SCSI "descriptor" sense format, active when true */
int dxfer_len;
- unsigned char * sensep;
int sense_len;
int scsi_status;
int resid;
@@ -82,7 +83,11 @@ struct sg_pt_win32_scsi {
int os_err; /* pseudo unix error */
int transport_err; /* windows error number */
int dev_fd; /* -1 for no "file descriptor" given */
- bool is_nvme;
+ uint32_t mdxfer_len;
+ uint32_t nvme_nsid; /* Valid range: 1 to 0xfffffffe */
+ unsigned char * dxferp;
+ unsigned char * mdxferp; /* NVMe has metadata buffer */
+ unsigned char * sensep;
union {
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb_d;
/* Last entry in structure so data buffer can be extended */
@@ -172,14 +177,14 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
share_mode = (O_EXCL & flags) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
/* lock */
for (k = 0; k < MAX_OPEN_SIMULT; k++)
- if (0 == handle_arr[k].in_use)
+ if (! handle_arr[k].in_use)
break;
if (k == MAX_OPEN_SIMULT) {
if (verbose)
pr2ws("too many open handles (%d)\n", MAX_OPEN_SIMULT);
return -EMFILE;
} else
- handle_arr[k].in_use = 1;
+ handle_arr[k].in_use = true;
/* unlock */
index = k;
shp = handle_arr + index;
@@ -212,7 +217,7 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
if (verbose)
pr2ws("expected format like: "
"'SCSI<port>:<bus>.<target>[.<lun>]'\n");
- shp->in_use = 0;
+ shp->in_use = false;
return -EINVAL;
}
got_scsi_name = 1;
@@ -240,7 +245,7 @@ scsi_pt_open_flags(const char * device_name, int flags, int verbose)
if (verbose)
pr2ws("Windows CreateFile error=%u\n",
(unsigned int)GetLastError());
- shp->in_use = 0;
+ shp->in_use = false;
return -ENODEV;
}
return index + WIN32_FDOFFSET;
@@ -259,7 +264,6 @@ scsi_pt_close_device(int device_fd)
int index;
index = device_fd - WIN32_FDOFFSET;
-
if ((index < 0) || (index >= WIN32_FDOFFSET))
return -ENODEV;
shp = handle_arr + index;
@@ -269,11 +273,82 @@ scsi_pt_close_device(int device_fd)
shp->target = 0;
shp->lun = 0;
memset(shp->adapter, 0, sizeof(shp->adapter));
- shp->in_use = 0;
+ shp->in_use = false;
shp->verbose = 0;
return 0;
}
+int
+check_pt_file_handle(int device_fd, const char * device_name, int verbose)
+{
+ int index;
+ const char * dnp = device_name ? device_name : "<empty>";
+ struct sg_pt_handle * shp;
+#if 0
+/* According to MSDN including <WinIoCtl.h> should define the following.
+ * At least when building under MinGW it doesn't, so comment out for now. */
+ STORAGE_PROPERTY_QUERY spq;
+ STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = {0};
+ DWORD bytes_ret = 0;
+ STORAGE_DESCRIPTOR_HEADER sdh = {0};
+#endif
+
+ if (verbose > 3)
+ pr2ws("%s: device_name: %s\n", __func__, dnp);
+ index = device_fd - WIN32_FDOFFSET;
+ if ((index < 0) || (index >= WIN32_FDOFFSET))
+ return -ENODEV;
+ shp = handle_arr + index;
+ if (! shp->in_use) {
+ pr2ws("%s: device_fd (%s) not in_use ??\n", __func__, dnp);
+ return -ENODEV;
+ }
+#if 0
+ /* test code from: http://codexpert.ro/blog/2013/10/26/get-
+ * physical-drive-serial-number-part-1/ */
+ memset(&spq, 0, sizeof(spq));
+ spq.PropertyId = StorageDeviceProperty;
+ spq.QueryType = PropertyStandardQuery;
+ if(! DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY,
+ &spq, sizeof(spq),
+ &sdh, sizeof(sdh), &bytes_ret, NULL)) {
+ uint32_t u;
+
+ u = (uint32_t)GetLastError();
+ if (verbose)
+ pr2ws("%s: Windows DeviceIoControl error=%u\n", __func__, u);
+ return -EIO; /* let app find transport error */
+ } else {
+ const DWORD out_buff_sz = sdh.Size;
+ uint8_t * outp;
+
+ outp = calloc(out_buff_sz, 1);
+ if (NULL == outp) {
+ pr2ws("%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+ if(! DeviceIoControl(shp->fh, IOCTL_STORAGE_QUERY_PROPERTY,
+ &spq, sizeof(spq),
+ outp, out_buff_sz, &bytes_ret, NULL)) {
+ uint32_t u = (uint32_t)GetLastError();
+
+ if (verbose)
+ pr2ws("%s: DeviceIoControl 2 error=%u\n", __func__, u);
+ return -EIO; /* let app find transport error */
+ } else {
+ STORAGE_DEVICE_DESCRIPTOR* ddp = (STORAGE_DEVICE_DESCRIPTOR*)outp;
+ const DWORD sn_off = ddp->SerialNumberOffset;
+
+ if (sn_off > 0)
+ pr2ws("%s: serial number: %.60s\n", __func__, outp + sn_off);
+ }
+ }
+#endif
+// xxxxxxxxxxxxx more code required <<<<<<<<<<<<<<<<
+ return 1; /* SCSI generic pass-though device */
+}
+
+
struct sg_pt_base *
construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
{
@@ -310,6 +385,8 @@ construct_scsi_pt_obj_with_fd(int dev_fd, int verbose)
else
free(psp);
}
+ if ((NULL == vp) && verbose)
+ pr2ws("%s: about to return NULL, space problem\n", __func__);
return vp;
}
@@ -434,6 +511,21 @@ set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
}
void
+set_pt_metadata_xfer(struct sg_pt_base * vp, unsigned char * mdxferp,
+ uint32_t mdxfer_len, bool out_true)
+{
+ struct sg_pt_win32_scsi * psp = vp->implp;
+
+ if (psp->mdxferp)
+ ++psp->in_err;
+ if (mdxfer_len > 0) {
+ psp->mdxferp = mdxferp;
+ psp->mdxfer_len = mdxfer_len;
+ psp->mdxfer_out = out_true;
+ }
+}
+
+void
set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)),
int pack_id __attribute__ ((unused)))
{
@@ -507,7 +599,7 @@ do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs,
return -psp->os_err;
}
shp = handle_arr + index;
- if (0 == shp->in_use) {
+ if (! shp->in_use) {
if (verbose)
pr2ws("File descriptor closed??\n");
psp->os_err = ENODEV;
@@ -590,21 +682,21 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs,
return SCSI_PT_DO_BAD_PARAMS;
}
if (device_fd < 0) {
- if (ptp->dev_fd < 0) {
+ if (psp->dev_fd < 0) {
if (verbose)
pr2ws("%s: No device file descriptor given\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
} else {
- if (ptp->dev_fd >= 0) {
- if (device_fd != ptp->dev_fd) {
+ if (psp->dev_fd >= 0) {
+ if (device_fd != psp->dev_fd) {
if (verbose)
pr2ws("%s: file descriptor given to create and this "
"differ\n", __func__);
return SCSI_PT_DO_BAD_PARAMS;
}
} else
- ptp->dev_fd = device_fd;
+ psp->dev_fd = device_fd;
}
if (0 == psp->swb_i.spt.CdbLength) {
if (verbose)
@@ -620,7 +712,7 @@ do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs,
return -psp->os_err;
}
shp = handle_arr + index;
- if (0 == shp->in_use) {
+ if (! shp->in_use) {
if (verbose)
pr2ws("File descriptor closed??\n");
psp->os_err = ENODEV;
@@ -800,6 +892,17 @@ pt_device_is_nvme(const struct sg_pt_base * vp)
return psp ? psp->is_nvme : false;
}
+/* If a NVMe block device (which includes the NSID) handle is associated
+ * * with 'vp', then its NSID is returned (values range from 0x1 to
+ * * 0xffffffe). Otherwise 0 is returned. */
+uint32_t
+get_pt_nvme_nsid(const struct sg_pt_base * vp)
+{
+ const struct sg_pt_win32_scsi * psp = vp->implp;
+
+ return psp->nvme_nsid;
+}
+
char *
get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
char * b)
diff --git a/sg3_utils.spec b/sg3_utils.spec
index 6a60354f..0d53d0e9 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Dec 04 2017 - dgilbert at interlog dot com
+* Mon Dec 18 2017 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.43
diff --git a/src/sg_inq.c b/src/sg_inq.c
index bfe3a5a0..d294406b 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -46,7 +46,7 @@
#include "sg_pt_nvme.h"
#endif
-static const char * version_str = "1.75 20171207"; /* SPC-5 rev 17 */
+static const char * version_str = "1.76 20171219"; /* SPC-5 rev 17 */
/* INQUIRY notes:
* It is recommended that the initial allocation length given to a
@@ -416,7 +416,7 @@ usage_for(const struct opts_t * op)
/* Processes command line options according to new option format. Returns
* 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
static int
-cl_new_process(struct opts_t * op, int argc, char * argv[])
+new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
int c, n;
@@ -561,7 +561,7 @@ cl_new_process(struct opts_t * op, int argc, char * argv[])
/* Processes command line options according to old option format. Returns
* 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
static int
-cl_old_process(struct opts_t * op, int argc, char * argv[])
+old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
bool jmp_out;
int k, plen, num, n;
@@ -733,7 +733,7 @@ cl_old_process(struct opts_t * op, int argc, char * argv[])
* of these options is detected (when processing the other format), processing
* stops and is restarted using the other format. Clear? */
static int
-cl_process(struct opts_t * op, int argc, char * argv[])
+parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
int res;
char * cp;
@@ -741,14 +741,14 @@ cl_process(struct opts_t * op, int argc, char * argv[])
cp = getenv("SG3_UTILS_OLD_OPTS");
if (cp) {
op->opt_new = false;
- res = cl_old_process(op, argc, argv);
+ res = old_parse_cmd_line(op, argc, argv);
if ((0 == res) && op->opt_new)
- res = cl_new_process(op, argc, argv);
+ res = new_parse_cmd_line(op, argc, argv);
} else {
op->opt_new = true;
- res = cl_new_process(op, argc, argv);
+ res = new_parse_cmd_line(op, argc, argv);
if ((0 == res) && (! op->opt_new))
- res = cl_old_process(op, argc, argv);
+ res = old_parse_cmd_line(op, argc, argv);
}
return res;
}
@@ -756,9 +756,9 @@ cl_process(struct opts_t * op, int argc, char * argv[])
#else /* SG_SCSI_STRINGS */
static int
-cl_process(struct opts_t * op, int argc, char * argv[])
+parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
- return cl_new_process(op, argc, argv);
+ return new_parse_cmd_line(op, argc, argv);
}
#endif /* SG_SCSI_STRINGS */
@@ -3812,9 +3812,14 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid,
set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp));
ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
if (vb > 2) {
+ int rlen;
+
pr2serr("do_scsi_pt() result is %d\n", ret);
- pr2serr("do_scsi_pt() result via sense buffer:\n");
- dStrHex((const char *)&cmd_back, sizeof(cmd_back), 0);
+ rlen = get_scsi_pt_sense_len(ptvp);
+ if (rlen > 0) {
+ pr2serr("do_scsi_pt() result via sense buffer:\n");
+ dStrHex((const char *)&cmd_back, rlen, 0);
+ }
}
if (ret)
return ret;
@@ -3915,9 +3920,14 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
set_scsi_pt_sense(ptvp, (unsigned char *)&cmd_back, sizeof(cmd_back));
ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
if (vb > 2) {
+ int rlen;
+
pr2serr("do_scsi_pt result is %d\n", ret);
- pr2serr("do_scsi_pt result via sense buffer:\n");
- dStrHex((const char *)&cmd_back, sizeof(cmd_back), 0);
+ rlen = get_scsi_pt_sense_len(ptvp);
+ if (rlen > 0) {
+ pr2serr("do_scsi_pt result via sense buffer:\n");
+ dStrHex((const char *)&cmd_back, rlen, 0);
+ }
}
if (ret)
goto err_out;
@@ -4043,11 +4053,17 @@ do_nvme_identify(int pt_fd, const struct opts_t * op)
{
pr2serr("%s: not implemented, no <linux/nvme_ioctl.h>\n", __func__);
if (op->do_verbose)
- pr2serr("Need to build on system with NVMe development headers\n");
+ pr2serr("Need to build on system with NVMe development headers "
+ "(pt_fd=%d)\n", pt_fd);
return 0;
}
#endif
+// <<<<<<<<<<<<<<<<<<<<<<<< ******************
+#include <linux/nvme_ioctl.h>
+#include "sg_pt_linux.h"
+// <<<<<<<<<<<<<<<<<<<<<<<< ******************
+
int
main(int argc, char * argv[])
@@ -4064,7 +4080,7 @@ main(int argc, char * argv[])
op->page_num = -1;
op->page_pdt = -1;
op->do_block = -1; /* use default for OS */
- res = cl_process(op, argc, argv);
+ res = parse_cmd_line(op, argc, argv);
if (res)
return SG_LIB_SYNTAX_ERROR;
if (op->do_help) {
@@ -4290,11 +4306,14 @@ main(int argc, char * argv[])
}
#endif
memset(rsp_buff, 0, sizeof(rsp_buff));
+
+#ifdef HAVE_NVME
n = check_pt_file_handle(sg_fd, op->device_name, op->do_verbose);
if ((3 == n) || (4 == n)) { /* NVMe char or NVMe block */
ret = do_nvme_identify(sg_fd, op);
goto fini;
}
+#endif
#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
if (op->do_ata) {
diff --git a/src/sg_ses.c b/src/sg_ses.c
index 96d25647..7b25d23b 100644
--- a/src/sg_ses.c
+++ b/src/sg_ses.c
@@ -32,7 +32,7 @@
* commands tailored for SES (enclosure) devices.
*/
-static const char * version_str = "2.25 20171208"; /* ses4r01 */
+static const char * version_str = "2.25 20171217"; /* ses4r01 */
#define MX_ALLOC_LEN ((64 * 1024) - 4) /* max allowable for big enclosures */
#define MX_ELEM_HDR 1024
@@ -1068,7 +1068,7 @@ parse_index(struct opts_t *op)
/* command line process, options and arguments. Returns 0 if ok. */
static int
-cl_process(struct opts_t *op, int argc, char *argv[])
+parse_cmd_line(struct opts_t *op, int argc, char *argv[])
{
int c, j, ret;
const char * data_arg = NULL;
@@ -5112,7 +5112,7 @@ main(int argc, char * argv[])
op->dev_slot_num = -1;
op->ind_indiv_last = -1;
pg_sz = sg_get_page_size();
- res = cl_process(op, argc, argv);
+ res = parse_cmd_line(op, argc, argv);
if (res)
return SG_LIB_SYNTAX_ERROR;
if (op->do_version) {
diff --git a/src/sg_write_x.c b/src/sg_write_x.c
index c036d378..79afe1f3 100644
--- a/src/sg_write_x.c
+++ b/src/sg_write_x.c
@@ -22,7 +22,6 @@
#include <ctype.h>
#include <sys/types.h> /* needed for lseek() */
#include <sys/stat.h>
-#include <sys/param.h> /* contains PAGE_SIZE */
#include <getopt.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
@@ -37,7 +36,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "1.06 20171208";
+static const char * version_str = "1.08 20171218";
/* Protection Information refers to 8 bytes of extra information usually
* associated with each logical block and is often abbreviated to PI while
@@ -74,7 +73,6 @@ static const char * version_str = "1.06 20171208";
#define DEF_RT 0xffffffff
#define DEF_AT 0xffff
#define DEF_TM 0xffff
-#define MAX_XFER_LEN (64 * 1024)
#define EBUFF_SZ 256
#define MAX_NUM_ADDR 128
@@ -130,67 +128,74 @@ static struct option long_options[] = {
};
struct opts_t {
- bool do_16;
+ bool do_16; /* default when --32 not given */
bool do_32;
- bool do_anchor;
- bool do_atomic; /* -A WRITE ATOMIC(16 or 32) */
+ bool do_anchor; /* from --unmap=U_A , bit 1; WRITE SAME */
+ bool do_atomic; /* selects WRITE ATOMIC(16 or 32) */
/* --atomic=AB AB --> .atomic_boundary */
bool do_combined; /* -c DOF --> .scat_lbdof */
- bool do_dry_run;
bool do_or; /* -O ORWRITE(16 or 32) */
bool do_scat_raw;
bool do_same; /* -M WRITE SAME(16 or 32) */
/* --same=NDOB NDOB --> .ndob */
bool do_scattered; /* -S WRITE SCATTERED(16 or 32) */
- /* --scattered=RD RD --> .scat_num_lbrd */
+ /* --scattered=RD RD --> .scat_num_lbard */
bool do_stream; /* -T WRITE STREAM(16 or 32) */
/* --stream=ID ID --> .str_id */
- bool do_unmap;
+ bool do_unmap; /* from --unmap=U_A , bit 0; WRITE SAME */
bool do_write_normal; /* -N WRITE (16 or 32) */
bool expect_pi_do; /* expect protection information (PI) which
* is 8 bytes long following each logical
* block in the data out buffer. */
- bool dpo;
- bool explicit_lba; /* .numblocks defaults to 0 when false */
- bool fua;
- bool ndob;
- bool strict;
- int dld; /* only used by WRITE(16) [why not WRITE(32) ?] */
- int grpnum;
+ bool dpo; /* "Disable Page Out" bit field */
+ bool fua; /* "Force Unit Access" bit field */
+ bool ndob; /* "No Data-Out Buffer" from --same=NDOB */
+ int dld; /* "Duration Limit Descrptor" bit mask; bit 0 -->
+ * DLD0, bit 1 --> DLD1, bit 2 --> DLD2
+ * only WRITE(16) and WRITE SCATTERED(16) */
+ int dry_run; /* temporary write when used more than once */
+ int grpnum; /* "Group Number", 0 to 0x3f */
int help;
int pi_type; /* -1: unknown: 0: type 0 (none): 1: type 1 */
- int timeout;
- int verbose;
+ int strict; /* > 0, report then exit on questionable meta data */
+ int timeout; /* timeout (in seconds) to abort SCSI commands */
+ int verbose; /* incremented for each -v */
int wrprotect; /* is ORPROTECT field for ORWRITE */
uint8_t bmop; /* bit mask operators for ORWRITE(32) */
uint8_t pgp; /* previous generation processing for ORWRITE(32) */
uint16_t app_tag; /* part of protection information (def: 0xffff) */
- uint16_t atomic_boundary;
- uint16_t scat_lbdof;
- uint16_t scat_num_lbrd;
+ uint16_t atomic_boundary; /* when 0 atomic write spans given length */
+ uint16_t scat_lbdof; /* by construction this must be >= 1 */
+ uint16_t scat_num_lbard; /* RD from --scattered=RD, number of LBA
+ * Range Descriptors */
uint16_t str_id; /* (stream ID) is for WRITE STREAM */
uint16_t tag_mask; /* part of protection information (def: 0xffff) */
uint32_t bs; /* logical block size (def: 0). 0 implies use READ
* CAPACITY(10 or 16) to determine */
- uint32_t bs_pi_do; /* logical block size plus PI, if any */
+ uint32_t bs_pi_do; /* logical block size plus PI, if any. This value is
+ * used as the actual block size */
uint32_t if_dlen; /* bytes to read after .if_offset from .if_name,
* if 0 given, read rest of .if_name */
- uint32_t numblocks; /* defaults to 0 if .explicit_lba is false, else
- * derives from IF and/or sum of NUMs */
- uint32_t orw_eog;
- uint32_t orw_nog;
+ uint32_t numblocks; /* defaults to 0, number of blocks (of user data) to
+ * write */
+ uint32_t orw_eog; /* from --generation=EOG,NOG (first argument) */
+ uint32_t orw_nog; /* from --generation=EOG,NOG (for ORWRITE) */
uint32_t ref_tag; /* part of protection information (def: 0xffffffff) */
- uint64_t lba;
+ uint64_t lba; /* "Logical Block Address", for non-scattered use */
uint64_t if_offset; /* byte offset in .if_name to start reading */
uint64_t tot_lbs; /* from READ CAPACITY */
ssize_t xfer_bytes; /* derived value: bs_pi_do * numblocks */
+ /* for WRITE SCATTERED .xfer_bytes < do_len */
const char * device_name;
- const char * if_name;
- const char * scat_filename;
+ const char * if_name; /* from --in=IF */
+ const char * scat_filename; /* from --scat-file=SF */
const char * cmd_name; /* e.g. 'Write atomic' */
- char cdb_name[24]; /* e.g. 'Write atomic (16)' */
+ char cdb_name[24]; /* e.g. 'Write atomic(16)' */
};
+static const uint32_t lbard_sz = 32;
+static const char * lbard_str = "LBA range descriptor";
+
static void
usage(int do_help)
@@ -199,7 +204,7 @@ usage(int do_help)
pr2serr("Usage:\n"
"sg_write_x [--16] [--32] [--app-tag=AT] [--atomic=AB] "
"[--bmop=OP,PGP]\n"
- " [--bs=LBS] [--combined=DOF] [--dld=DLD] [--dpo] "
+ " [--bs=BS] [--combined=DOF] [--dld=DLD] [--dpo] "
"[--dry-run]\n"
" [--fua] [--generation=EOG,NOG] [--grpnum=GN] "
"[--help] --in=IF\n"
@@ -213,7 +218,7 @@ usage(int do_help)
" [--verbose] [--version] [--wrprotect=WRP] DEVICE\n");
if (1 != do_help) {
pr2serr("\nOr the corresponding short option usage:\n"
- "sg_write_x [-6] [-3] [-a AT] [-A AB] [-B OP,PGP] [-b LBS] "
+ "sg_write_x [-6] [-3] [-a AT] [-A AB] [-B OP,PGP] [-b BS] "
"[-c DOF] [-D DLD]\n"
" [-d] [-x] [-f] [-G EOG,NOG] [-g GN] [-h] -i IF "
"[-l LBA,LBA...]\n"
@@ -240,19 +245,22 @@ usage(int do_help)
" Previous\n"
" Generation Processing field "
"to PGP\n"
- " --bs=LBS|-b LBS logical block size (def: use READ "
- "CAPACITY)\n"
+ " --bs=BS|-b BS block size (def: use READ CAPACITY), "
+ "if power of\n"
+ " 2: logical block size, otherwise: "
+ "actual block size\n"
" --combined=DOF|-c DOF scatter list and data combined "
"for WRITE\n"
" SCATTERED, data starting at "
"offset DOF which\n"
" has units of sizeof(LB+PI); "
- "sizeof(PI)=8 or 0\n"
+ "sizeof(PI)=8n or 0\n"
" --dld=DLD|-D DLD set duration limit descriptor (dld) "
"bits (def: 0)\n"
" --dpo|-d set DPO (disable page out) field "
"(def: clear)\n"
- " --dry-run|-x exit just before sending SCSI command\n"
+ " --dry-run|-x exit just before sending SCSI write "
+ "command\n"
" --fua|-f set FUA (force unit access) field "
"(def: clear)\n"
" --generation=EOG,NOG set Expected ORWgeneration field "
@@ -261,7 +269,8 @@ usage(int do_help)
"NOG\n"
);
pr2serr(
- " --grpnum=GN|-g GN GN is group number field (def: 0)\n"
+ " --grpnum=GN|-g GN GN is group number field (def: 0, "
+ "range: 0 to 31)\n"
" --help|-h use multiple times for different "
"usage messages\n"
" --in=IF|-i IF IF is file to fetch NUM blocks of "
@@ -331,7 +340,7 @@ usage(int do_help)
} else if (2 == do_help) {
printf("WRITE ATOMIC (16 or 32) applicable options:\n"
" sg_write_x --atomic=AB --in=IF [--16] [--32] [--app-tag=AT] "
- "[--bs=LBS]\n"
+ "[--bs=BS]\n"
" [--dpo] [--fua] [--grpnum=GN] [--lba=LBA] "
"[--num=NUM]\n"
" [--offset=OFF[,DLEN]] [--ref-tag=RT] [--strict] "
@@ -339,7 +348,7 @@ usage(int do_help)
" [--timeout=TO] [--wrprotect=WRP] DEVICE\n"
"\n"
"normal WRITE (32) applicable options:\n"
- " sg_write_x --normal --in=IF --32 [--app-tag=AT] [--bs=LBS] "
+ " sg_write_x --normal --in=IF --32 [--app-tag=AT] [--bs=BS] "
"[--dpo] [--fua]\n"
" [--grpnum=GN] [--lba=LBA] [--num=NUM] "
"[--offset=OFF[,DLEN]]\n"
@@ -348,7 +357,7 @@ usage(int do_help)
" [--wrprotect=WRP] DEVICE\n"
"\n"
"normal WRITE (16) applicable options:\n"
- " sg_write_x --normal --in=IF [--16] [--bs=LBS] [--dld=DLD] "
+ " sg_write_x --normal --in=IF [--16] [--bs=BS] [--dld=DLD] "
"[--dpo] [--fua]\n"
" [--grpnum=GN] [--lba=LBA] [--num=NUM] "
"[--offset=OFF[,DLEN]]\n"
@@ -356,7 +365,7 @@ usage(int do_help)
"[--wrprotect=WRP] DEVICE\n"
"\n"
"ORWRITE (32) applicable options:\n"
- " sg_write_x --or --in=IF --32 [--bmop=OP,PGP] [--bs=LBS] "
+ " sg_write_x --or --in=IF --32 [--bmop=OP,PGP] [--bs=BS] "
"[--dpo] [--fua]\n"
" [--generation=EOG,NOG] [--grpnum=GN] [--lba=LBA] "
"[--num=NUM]\n"
@@ -364,7 +373,7 @@ usage(int do_help)
" [--wrprotect=ORP] DEVICE\n"
"\n"
"ORWRITE (16) applicable options:\n"
- " sg_write_x --or --in=IF [--16] [--bs=LBS] [--dpo] [--fua] "
+ " sg_write_x --or --in=IF [--16] [--bs=BS] [--dpo] [--fua] "
"[--grpnum=GN]\n"
" [--lba=LBA] [--num=NUM] [--offset=OFF[,DLEN]] "
"[--strict]\n"
@@ -373,7 +382,7 @@ usage(int do_help)
);
} else if (3 == do_help) {
printf("WRITE SAME (32) applicable options:\n"
- " sg_write_x --same=NDOB --32 [--app-tag=AT] [--bs=LBS] "
+ " sg_write_x --same=NDOB --32 [--app-tag=AT] [--bs=BS] "
"[--grpnum=GN]\n"
" [--in=IF] [--lba=LBA] [--num=NUM] "
"[--offset=OFF[,DLEN]]\n"
@@ -383,7 +392,7 @@ usage(int do_help)
"\n"
"WRITE SCATTERED (32) applicable options:\n"
" sg_write_x --scattered --in=IF --32 [--app-tag=AT] "
- "[--bs=LBS]\n"
+ "[--bs=BS]\n"
" [--combined=DOF] [--dpo] [--fua] [--grpnum=GN]\n"
" [--lba=LBA,LBA...] [--num=NUM,NUM...] "
"[--offset=OFF[,DLEN]]\n"
@@ -393,7 +402,7 @@ usage(int do_help)
"DEVICE\n"
"\n"
"WRITE SCATTERED (16) applicable options:\n"
- " sg_write_x --scattered --in=IF [--bs=LBS] [--combined=DOF] "
+ " sg_write_x --scattered --in=IF [--bs=BS] [--combined=DOF] "
"[--dld=DLD]\n"
" [--dpo] [--fua] [--grpnum=GN] [--lba=LBA,LBA...]\n"
" [--num=NUM,NUM...] [--offset=OFF[,DLEN]] "
@@ -404,7 +413,7 @@ usage(int do_help)
"\n"
"WRITE STREAM (32) applicable options:\n"
" sg_write_x --stream=ID --in=IF --32 [--app-tag=AT] "
- "[--bs=LBS] [--dpo]\n"
+ "[--bs=BS] [--dpo]\n"
" [--fua] [--grpnum=GN] [--lba=LBA] [--num=NUM]\n"
" [--offset=OFF[,DLEN]] [--ref-tag=RT] [--strict] "
"[--tag-mask=TM]\n"
@@ -412,7 +421,7 @@ usage(int do_help)
"DEVICE\n"
"\n"
"WRITE STREAM (16) applicable options:\n"
- " sg_write_x --stream=ID --in=IF [--16] [--bs=LBS] [--dpo] "
+ " sg_write_x --stream=ID --in=IF [--16] [--bs=BS] [--dpo] "
"[--fua]\n"
" [--grpnum=GN] [--lba=LBA] [--num=NUM] "
"[--offset=OFF[,DLEN]]\n"
@@ -430,7 +439,7 @@ usage(int do_help)
" --dry-run option\n"
" - all WRITE X commands will accept --scat-file=SF and "
"optionally --scat-raw\n"
- " options but only the first addr,num pair is used (any "
+ " options but only the first lba,num pair is used (any "
"more are ignored)\n"
" - when '--rscat-aw --scat-file=SF' are used then the binary "
"format expected in\n"
@@ -466,6 +475,27 @@ usage(int do_help)
}
}
+static int
+bin_read(int fd, uint8_t * up, uint32_t len, const char * fname)
+{
+ int res, err;
+
+pr2serr("%s: len=%u, fname: %s\n", __func__, len, fname);
+ res = read(fd, up, len);
+ if (res < 0) {
+ err = errno;
+ pr2serr("Error doing read of %s file: %s\n", fname,
+ safe_strerror(err));
+ return SG_LIB_FILE_ERROR;
+ }
+ if ((uint32_t)res < len) {
+ pr2serr("Short (%u) read of %s file, wanted %u\n", (unsigned int)res,
+ fname, len);
+ return SG_LIB_FILE_ERROR;
+ }
+ return 0;
+}
+
/* Returns true if num_of_f_chars of ASCII 'f' or 'F' characters are found
* in sequence. Any leading "0x" or "0X" is ignored; otherwise false is
* returned (and the comparison stops when the first mismatch is found).
@@ -613,8 +643,8 @@ build_num_arr(const char * inp, uint32_t * num_arr, uint32_t * num_arr_len,
* SG_LIB_SYNTAX_ERROR). If protection information fields not given, then
* default values are given (i.e. all 0xff bytes). Ignores all spaces and
* tabs and everything after '#' on lcp (assumed to be an ASCII line that
- * is null terminated). If successful writes a LBA range descriptor starting
- * at 'up'. The array starting at 'up' should be at least 20 bytes long. */
+ * is null terminated). If successful and 'up' is non NULL then writes a
+ * LBA range descriptor starting at 'up'. */
static int
parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
{
@@ -641,7 +671,8 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
pr2serr("%s: error reading LBA (first) item on ", __func__);
return SG_LIB_SYNTAX_ERROR;
}
- sg_put_unaligned_be64((uint64_t)ll, up + 0);
+ if (up)
+ sg_put_unaligned_be64((uint64_t)ll, up + 0);
ok = false;
cp = strchr(bp, ',');
if (cp) {
@@ -656,8 +687,10 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
pr2serr("%s: error reading NUM (second) item on ", __func__);
return SG_LIB_SYNTAX_ERROR;
}
- sg_put_unaligned_be32((uint32_t)ll, up + 8);
- *sum_num += (uint32_t)ll;
+ if (up)
+ sg_put_unaligned_be32((uint32_t)ll, up + 8);
+ if (sum_num)
+ *sum_num += (uint32_t)ll;
/* now for 3 PI items */
for (k = 0; k < 3; ++k) {
ok = true;
@@ -690,21 +723,21 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
if (ll > UINT32_MAX) {
pr2serr("%s: error with item 3, >0xffffffff; on ", __func__);
ok = false;
- } else
+ } else if (up)
sg_put_unaligned_be32((uint32_t)ll, up + 12);
break;
case 1:
if (ll > UINT16_MAX) {
pr2serr("%s: error with item 4, >0xffff; on ", __func__);
ok = false;
- } else
+ } else if (up)
sg_put_unaligned_be16((uint16_t)ll, up + 16);
break;
case 2:
if (ll > UINT16_MAX) {
pr2serr("%s: error with item 5, >0xffff; on ", __func__);
ok = false;
- } else
+ } else if (up)
sg_put_unaligned_be16((uint16_t)ll, up + 18);
break;
default:
@@ -720,13 +753,16 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
for ( ; k < 3; ++k) {
switch (k) {
case 0:
- sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12);
+ if (up)
+ sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12);
break;
case 1:
- sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16);
+ if (up)
+ sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16);
break;
case 2:
- sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18);
+ if (up)
+ sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18);
break;
default:
pr2serr("%s: k=%d should not be >= 3\n", __func__, k);
@@ -740,44 +776,47 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num)
/* Read pairs or quintets from a scat_file and places them in a T10 scatter
* list array is built starting at at t10_scat_list_out (i.e. as per T10 the
* first 32 bytes are zeros followed by the first LBA range descriptor (also
- * 32 bytes long) then the second LBA range descriptor, etc. If do_16 is
- * true then only LBA,NUM pairs are expected, loosely formatted with
- * numbers found alternating between LBA and NUM, with an even number of
- * elements required overall. If do_16 is false then a stricter format for
- * quintets is expected: each non comment line should contain: LBA,NUM[,RT,
- * AT,TM] . If RT,AT,TM are not given then they assume their defaults (i.e.
- * 0xffffffff, 0xffff, 0xffff). Each number (64 bits for the LBA, 32 bits for
- * NUM and RT, 16 bit for AT and TM) may be a comma, space or tab separated
- * list. Assumed decimal unless prefixed by '0x', '0X' or contains trailing
- * 'h' or 'H' (which indicate hex). Returns 0 if ok, else error number. If
- * ok also yields the actual byte length of t10_scat_list_out written into
- * act_list_blen and the number of LBA range descriptors written in
- * num_scat_elems . If parse_one is true then exits after one LBA range
- * descriptor is decoded. */
+ * 32 bytes long) then the second LBA range descriptor, etc. The pointer
+ * t10_scat_list_out may be NULL in which case the T10 list array is not
+ * built but all other operations take place; this can be useful for sizing
+ * how large the area holding that list needs to be. The max_list_blen may
+ * also be 0. If do_16 is true then only LBA,NUM pairs are expected,
+ * loosely formatted with numbers found alternating between LBA and NUM, with
+ * an even number of elements required overall. If do_16 is false then a
+ * stricter format for quintets is expected: each non comment line should
+ * contain: LBA,NUM[,RT,AT,TM] . If RT,AT,TM are not given then they assume
+ * their defaults (i.e. 0xffffffff, 0xffff, 0xffff). Each number (64 bits for
+ * the LBA, 32 bits for NUM and RT, 16 bit for AT and TM) may be a comma,
+ * space or tab separated list. Assumed decimal unless prefixed by '0x', '0X'
+ * or contains trailing 'h' or 'H' (which indicate hex). Returns 0 if ok,
+ * else error number. If ok also yields the number of LBA range descriptors
+ * written in num_scat_elems and the sum of NUM elements found. Note that
+ * sum_num is not initialized to 0. If parse_one is true then exits
+ * after one LBA range descriptor is decoded. */
static int
build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
- uint8_t * t10_scat_list_out, uint32_t * act_list_blen,
- uint32_t * num_scat_elems, uint32_t * sum_num,
- int max_list_blen)
+ uint8_t * t10_scat_list_out, uint16_t * num_scat_elems,
+ uint32_t * sum_num, uint32_t max_list_blen)
{
bool have_stdin = false;
bool bit0, ok;
int off = 0;
- int in_len, k, j, m, n, ind, res;
+ int in_len, k, j, m, n, res;
int64_t ll;
char * lcp;
uint8_t * up = t10_scat_list_out;
FILE * fp = NULL;
char line[1024];
- if ((NULL == scat_fname) || (NULL == up) || (NULL == act_list_blen) ||
- (NULL == num_scat_elems) || (max_list_blen < 64)) {
- pr2serr("%s: failed sanity\n", __func__);
- return 1;
- } else {
+pr2serr("%s: max_list_blen=%u, have t10_scat_list_out pointer=%u\n", __func__, max_list_blen, (t10_scat_list_out ? 1 : 0));
+ if (up) {
+ if (max_list_blen < 64) {
+ pr2serr("%s: t10_scat_list_out is too short\n", __func__);
+ return SG_LIB_SYNTAX_ERROR;
+ }
memset(up, 0, max_list_blen);
- n = 32;
}
+ n = lbard_sz;
have_stdin = ((1 == strlen(scat_fname)) && ('-' == scat_fname[0]));
if (have_stdin) {
@@ -787,10 +826,12 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
fp = fopen(scat_fname, "r");
if (NULL == fp) {
pr2serr("%s: unable to open %s\n", __func__, scat_fname);
- return 1;
+ return SG_LIB_FILE_ERROR;
}
}
- for (j = 0; j < 1024; ++j) { /* loop over each line in file */
+ for (j = 0; j < 1024; ++j) {/* loop over lines in file */
+ if ((max_list_blen > 0) && ((n + lbard_sz) > max_list_blen))
+ goto fini;
if (NULL == fgets(line, sizeof(line), fp))
break;
// could improve with carry_over logic if sizeof(line) too small
@@ -818,14 +859,16 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
goto bad_exit;
}
if (! do_16) {
- res = parse_scat_pi_line(lcp, up + n, sum_num);
+ res = parse_scat_pi_line(lcp, up ? (up + n) : up, sum_num);
if (999 == res)
;
else if (0 == res) {
- n += 32;
+ n += lbard_sz;
if (parse_one)
- goto just_one;
+ goto fini;
} else {
+ if (SG_LIB_CAT_NOT_READY == res)
+ goto bad_mem_exit;
pr2serr("line %d in %s\n", j + 1, scat_fname);
goto bad_exit;
}
@@ -835,13 +878,7 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
ll = sg_get_llnum(lcp);
ok = ((-1 != ll) || all_ascii_f_s(lcp, 16));
if (ok) {
- ind = ((off + k) >> 1);
bit0 = !! (0x1 & (off + k));
- if (ind >= max_list_blen) {
- pr2serr("%s: array length exceeded in %s\n", __func__,
- scat_fname);
- goto bad_exit;
- }
if (bit0) {
if (ll > UINT32_MAX) {
pr2serr("%s: number exceeds 32 bits in line %d, at "
@@ -849,13 +886,17 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
(int)(lcp - line + 1), scat_fname);
goto bad_exit;
}
- sg_put_unaligned_be32((uint32_t)ll, up + n + 8);
- *sum_num += (uint32_t)ll;
- n += 32; /* skip to next LBA range descriptor */
+ if (up)
+ sg_put_unaligned_be32((uint32_t)ll, up + n + 8);
+ if (sum_num)
+ *sum_num += (uint32_t)ll;
+ n += lbard_sz; /* skip to next LBA range descriptor */
if (parse_one)
- goto just_one;
- } else
- sg_put_unaligned_be64((uint64_t)ll, up + n + 0);
+ goto fini;
+ } else {
+ if (up)
+ sg_put_unaligned_be64((uint64_t)ll, up + n + 0);
+ }
lcp = strpbrk(lcp, " ,\t");
if (NULL == lcp)
break;
@@ -879,16 +920,20 @@ build_t10_scat(const char * scat_fname, bool do_16, bool parse_one,
"%s\n", __func__, scat_fname);
goto bad_exit;
}
-just_one:
- *act_list_blen = n - 32;
- *num_scat_elems = (n / 32) - 1;
+fini:
+ *num_scat_elems = (n / lbard_sz) - 1;
+pr2serr("%s: num_scat_elems=%u\n", __func__, *num_scat_elems);
if (fp && (stdin != fp))
fclose(fp);
return 0;
bad_exit:
if (fp && (stdin != fp))
fclose(fp);
- return 1;
+ return SG_LIB_SYNTAX_ERROR;
+bad_mem_exit:
+ if (fp && (stdin != fp))
+ fclose(fp);
+ return SG_LIB_CAT_NOT_READY; /* flag output buffer too small */
}
static bool
@@ -900,10 +945,10 @@ is_pi_default(const struct opts_t * op)
/* Given a t10 parameter list header (32 zero bytes) for WRITE SCATTERED
* (16 or 32) followed by n RDs with a total length of at least
- * max_lbrds_blen bytes, find "n" and increment where num_lbrds points
+ * max_lbrds_blen bytes, find "n" and increment where num_lbard points
* n times. Further get the LBA length component from each RD and add each
* length into where sum_num points. Note: the caller probably wants to zero
- * where num_lbrds and sum_num point before invoking this function. If all
+ * where num_lbard and sum_num point before invoking this function. If all
* goes well return true, else false. If a degenerate RD is detected then
* if 'RD' (from --scattered=RD) is 0 then stop looking for further RDs;
* otherwise keep going. Currently overlapping LBA range descriptors are no
@@ -916,40 +961,42 @@ is_pi_default(const struct opts_t * op)
* the number of RDs found then a report is sent to stderr and if op->strict
* > 0 then returns false, else returns true. */
static bool
-check_lbrds(const uint8_t * up, int max_lbrds_blen, const struct opts_t * op,
- uint32_t * num_lbrds, uint32_t * sum_num)
+check_lbrds(const uint8_t * up, uint32_t max_lbrds_blen,
+ const struct opts_t * op, uint16_t * num_lbard,
+ uint32_t * sum_num)
{
bool ok;
int k, j, n;
- const int max_lbrd_start = max_lbrds_blen - 32;
+ const int max_lbrd_start = max_lbrds_blen - lbard_sz;
int vb = op->verbose;
+pr2serr("%s: max_lbrds_blen=%u\n", __func__, max_lbrds_blen);
if (op->strict) {
- if (max_lbrds_blen < 32) {
- pr2serr("%s: logical block range descriptors too short "
- "(%d < 32)\n", __func__, max_lbrds_blen);
+ if (max_lbrds_blen < lbard_sz) {
+ pr2serr("%s: %ss too short (%d < 32)\n", __func__, lbard_str,
+ max_lbrds_blen);
return false;
}
- if (! sg_all_zeros(up, 32)) {
+ if (! sg_all_zeros(up, lbard_sz)) {
pr2serr("%s: first 32 bytes of WRITE SCATTERED data-out buffer "
"should be zero.\nFound non-zero byte.\n", __func__);
return false;
}
}
- if (max_lbrds_blen < 64) {
- *num_lbrds = 0;
+ if (max_lbrds_blen < (2 * lbard_sz)) {
+ *num_lbard = 0;
return true;
}
- n = op->scat_num_lbrd ? -1 : (int)op->scat_num_lbrd;
- for (k = 32, j = 0; k < max_lbrd_start; k += 32) {
+ n = op->scat_num_lbard ? (int)op->scat_num_lbard : -1;
+ for (k = lbard_sz, j = 0; k < max_lbrd_start; k += lbard_sz, ++j) {
if ((n < 0) && sg_all_zeros(up + k + 0, 12)) { /* degenerate LBA */
if (vb) /* ... range descriptor terminator if --scattered=0 */
- pr2serr("%s: degenerate LBA range descriptor stops scan at "
- "k=%d (RD=0)\n", __func__, k);
+ pr2serr("%s: degenerate %s stops scan at k=%d (num_rds=%d)\n",
+ __func__, lbard_str, k, j);
break;
}
*sum_num += sg_get_unaligned_be32(up + k + 8);
- *num_lbrds += 1;
+ *num_lbard += 1;
if (op->strict) {
ok = true;
if (op->wrprotect) {
@@ -958,49 +1005,53 @@ check_lbrds(const uint8_t * up, int max_lbrds_blen, const struct opts_t * op,
} else if (! sg_all_zeros(up + k + 12, 20))
ok = false;
if (! ok) {
- pr2serr("%s: LB range descriptor %d non zero in reserved "
- "fields\n", __func__, (k / 32) - 1);
+ pr2serr("%s: %s %d non zero in reserved fields\n", __func__,
+ lbard_str, (k / lbard_sz) - 1);
return false;
}
}
- ++j;
if (n >= 0) {
if (--n <= 0)
break;
}
}
if ((k < max_lbrd_start) && op->strict) { /* check pad all zeros */
- k += 32;
- n = max_lbrds_blen - k;
- if (! sg_all_zeros(up + k, n)) {
- pr2serr("%s: pad (%d bytes) following LB range descriptors is "
- "non zero\n", __func__, n);
+ k += lbard_sz;
+ j = max_lbrds_blen - k;
+ if (! sg_all_zeros(up + k, j)) {
+ pr2serr("%s: pad (%d bytes) following %ss is non zero\n",
+ __func__, j, lbard_str);
return false;
}
}
if (vb > 2)
- pr2serr("%s: about to return true, num_lbrds=%u, sum_num=%u "
- "[k=%d, n=%d]\n", __func__, *num_lbrds, *sum_num, k, n);
- if (n > 0) {
- pr2serr("%s: number of range descriptors found (%d) less than RD "
- "(%u) given to --scattered=\n", __func__, j,
- op->scat_num_lbrd);
- if (op->strict)
- return false;
- }
+ pr2serr("%s: about to return true, num_lbard=%u, sum_num=%u "
+ "[k=%d, n=%d]\n", __func__, *num_lbard, *sum_num, k, n);
return true;
}
static int
+sum_num_lbards(const uint8_t * up, int num_lbards)
+{
+ int sum = 0;
+ int k, n;
+
+ for (k = 0, n = lbard_sz; k < num_lbards; ++k, n += lbard_sz)
+ sum += sg_get_unaligned_be32(up + n + 8);
+ return sum;
+}
+
+static int
do_write_x(int sg_fd, const void * dataoutp, int dout_len,
const struct opts_t * op)
{
- int k, ret, res, sense_cat, cdb_len;
+ int k, ret, res, sense_cat, cdb_len, vb;
uint8_t x_cdb[WRITE_X_32_LEN]; /* use for both lengths */
uint8_t sense_b[SENSE_BUFF_LEN];
struct sg_pt_base * ptvp;
memset(x_cdb, 0, sizeof(x_cdb));
+ vb = op->verbose;
cdb_len = op->do_16 ? WRITE_X_16_LEN : WRITE_X_32_LEN;
if (16 == cdb_len) {
if (! op->do_scattered)
@@ -1135,9 +1186,12 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
x_cdb[2] |= 0x1;
}
sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 4);
- sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 8);
+ sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 8);
+ /* Spec says Buffer Transfer Length field (BTL) is the number
+ * of (user) Logical Blocks in the data-out buffer and that BTL
+ * may be 0. So the total data-out buffer length in bytes is:
+ * (scat_lbdof + numblocks) * actual_block_size */
sg_put_unaligned_be32(op->numblocks, x_cdb + 10);
-
} else {
sg_put_unaligned_be16((uint16_t)WRITE_SCATTERED32_SA, x_cdb + 8);
x_cdb[10] = ((op->wrprotect & 0x7) << 5);
@@ -1146,7 +1200,7 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
if (op->fua)
x_cdb[10] |= 0x8;
sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 12);
- sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 16);
+ sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 16);
sg_put_unaligned_be32(op->numblocks, x_cdb + 28);
/* ref_tag, app_tag and tag_mask placed in scatter list */
}
@@ -1178,43 +1232,70 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
return -1;
}
- if (op->verbose > 1) {
+ if (vb > 1) {
pr2serr(" %s cdb: ", op->cdb_name);
for (k = 0; k < cdb_len; ++k)
pr2serr("%02x ", x_cdb[k]);
pr2serr("\n");
}
- if (op->do_scattered && (op->verbose > 2) && (dout_len > 63)) {
+ if (op->do_scattered && (vb > 2) && (dout_len > 31)) {
uint32_t sod_off = op->bs_pi_do * op->scat_lbdof;
const uint8_t * up = (const uint8_t *)dataoutp;
- pr2serr(" %s scatter list, number of LBA range descriptors: %u\n",
- op->cdb_name, op->scat_num_lbrd);
+ pr2serr(" %s scatter list, number of %ss: %u\n", op->cdb_name,
+ lbard_str, op->scat_num_lbard);
pr2serr(" byte offset of data_to_write: %u, dout_len: %d\n",
sod_off, dout_len);
- up += 32; /* step over parameter list header */
- for (k = 0; k < (int)op->scat_num_lbrd; ++k, up += 32) {
+ up += lbard_sz; /* step over parameter list header */
+ for (k = 0; k < (int)op->scat_num_lbard; ++k, up += lbard_sz) {
pr2serr(" desc %d: LBA=0x%" PRIx64 " numblocks=%" PRIu32
"%s", k, sg_get_unaligned_be64(up + 0),
sg_get_unaligned_be32(up + 8), (op->do_16 ? "\n" : " "));
- if (! op->do_16)
+ if (op->do_32)
pr2serr("rt=0x%x at=0x%x tm=0x%x\n",
sg_get_unaligned_be32(up + 12),
sg_get_unaligned_be16(up + 16),
sg_get_unaligned_be16(up + 18));
- if ((uint32_t)(((k + 2) * 32) + 20) > sod_off)
+ if ((uint32_t)(((k + 2) * lbard_sz) + 20) > sod_off)
pr2serr("Warning: possible clash of descriptor %u with "
"data_to_write\n", k);
}
}
- if ((op->verbose > 3) && (dout_len > 0)) {
- pr2serr(" Data-out buffer contents:\n");
- dStrHexErr((const char *)dataoutp, op->xfer_bytes, 1);
+ if ((vb > 3) && (dout_len > 0)) {
+ if ((dout_len > 1024) && (vb < 7)) {
+ pr2serr(" Data-out buffer contents (first 1024 of %u "
+ "bytes):\n", dout_len);
+ dStrHexErr((const char *)dataoutp, 1024, 1);
+ pr2serr(" Above: dout's first 1024 of %u bytes [%s]\n",
+ dout_len, op->cdb_name);
+ } else {
+ pr2serr(" Data-out buffer contents (length=%u):\n", dout_len);
+ dStrHexErr((const char *)dataoutp, (int)dout_len, 1);
+ }
}
- if (op->do_dry_run) {
- if (op->verbose)
- pr2serr("Exit just before sending command+data due to "
- "--dry-run\n");
+ if (op->dry_run) {
+ if (vb)
+ pr2serr("Exit just before sending %s due to --dry-run\n",
+ op->cdb_name);
+ if (op->dry_run > 1) {
+ int w_fd;
+ const char * w_fname = "sg_write_x.bin";
+
+ w_fd = open(w_fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (w_fd < 0) {
+ perror(w_fname);
+ return SG_LIB_FILE_ERROR;
+ }
+ res = write(w_fd, dataoutp, dout_len);
+ if (res < 0) {
+ perror(w_fname);
+ close(w_fd);
+ return SG_LIB_FILE_ERROR;
+ }
+ if (vb)
+ pr2serr("Wrote data-out buffer to %s\n", w_fname);
+ close(w_fd);
+ }
return 0;
}
ptvp = construct_scsi_pt_obj();
@@ -1224,11 +1305,14 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
}
set_scsi_pt_cdb(ptvp, x_cdb, cdb_len);
set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
- set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, op->xfer_bytes);
- res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose);
+ if (dout_len > 0)
+ set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, dout_len);
+ else if (vb)
+ pr2serr("%s thinks dout_len==0, so empty dout buffer\n",
+ op->cdb_name);
+ res = do_scsi_pt(ptvp, sg_fd, op->timeout, vb);
ret = sg_cmds_process_resp(ptvp, op->cdb_name, res, SG_NO_DATA_IN,
- sense_b, true /*noisy */, op->verbose,
- &sense_cat);
+ sense_b, true /*noisy */, vb, &sense_cat);
if (-1 == ret)
; /* general (OS) error like ioctl not recognized */
else if (-2 == ret) {
@@ -1247,9 +1331,13 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len,
valid = sg_get_sense_info_fld(sense_b, slen, &ull);
if (valid) {
pr2serr("Medium or hardware error starting at ");
- if (op->do_scattered)
- pr2serr("scatter descriptor=%" PRIu64 "]\n", ull);
- else
+ if (op->do_scattered) {
+ if (0 == ull)
+ pr2serr("%s=<not reported>\n", lbard_str);
+ else
+ pr2serr("%s=%" PRIu64 "] (origin 0)\n", lbard_str,
+ ull - 1);
+ } else
pr2serr("lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull,
ull);
}
@@ -1284,6 +1372,8 @@ do_read_capacity(int sg_fd, struct opts_t *op)
true, (vb ? (vb - 1): 0));
}
if (0 == res) {
+ uint32_t pi_len = 0;
+
if (vb > 3) {
pr2serr("Read capacity(16) response:\n");
dStrHexErr((const char *)resp_buff, RCAP16_RESP_LEN, 1);
@@ -1292,14 +1382,18 @@ do_read_capacity(int sg_fd, struct opts_t *op)
op->tot_lbs = sg_get_unaligned_be64(resp_buff + 0) + 1;
prot_en = !!(resp_buff[12] & 0x1);
if (prot_en) {
+ uint32_t pi_exp;
+
op->pi_type = ((resp_buff[12] >> 1) & 0x7) + 1;
+ pi_exp = 0xf & (resp_buff[13] >> 4);
+ pi_len = 8 * (1 << pi_exp);
if (op->wrprotect > 0) {
- op->bs_pi_do = op->bs + 8;
+ op->bs_pi_do = op->bs + pi_len;
if (vb > 1)
pr2serr(" For data out buffer purposes the effective "
"block size is %u (lb size\n is %u) because "
- "PROT_EN=1 and WRPROTECT>0\n", op->bs,
- op->bs_pi_do);
+ "PROT_EN=1, PI_EXP=%u and WRPROTECT>0\n", op->bs,
+ pi_exp, op->bs_pi_do);
}
} else { /* device formatted to PI type 0 (i.e. none) */
op->pi_type = 0;
@@ -1317,18 +1411,17 @@ do_read_capacity(int sg_fd, struct opts_t *op)
uint8_t d[2];
pr2serr("Read capacity(16) response fields:\n");
- pr2serr(" Last_LBA=0x%" PRIx64 ", LB size: %u (with PI: "
- "%u) bytes, p_type=%u\n", op->tot_lbs - 1, op->bs,
- op->bs + (prot_en ? 8 : 0),
+ pr2serr(" Last_LBA=0x%" PRIx64 " LB size: %u (with PI: "
+ "%u) bytes p_type=%u\n", op->tot_lbs - 1,
+ op->bs, op->bs + (prot_en ? pi_len : 0),
((resp_buff[12] >> 1) & 0x7));
- pr2serr(" prot_en=%u (PI type=%u), p_i_exp=%u, "
- "lbppb_exp=%u, lbpme=%u, ",
- prot_en, op->pi_type,
- ((resp_buff[13] >> 4) & 0xf),
- (resp_buff[13] & 0xf), (resp_buff[14] & 0x80));
+ pr2serr(" prot_en=%u [PI type=%u] p_i_exp=%u lbppb_exp=%u "
+ "lbpme,rz=%u,", prot_en, op->pi_type,
+ ((resp_buff[13] >> 4) & 0xf), (resp_buff[13] & 0xf),
+ !!(resp_buff[14] & 0x80));
memcpy(d, resp_buff + 14, 2);
d[0] &= 0x3f;
- pr2serr("lbprz=%u, low_ali_lba=%u\n", (resp_buff[14] & 0x40),
+ pr2serr("%u low_ali_lba=%u\n", !!(resp_buff[14] & 0x40),
sg_get_unaligned_be16(d));
}
} else if ((SG_LIB_CAT_INVALID_OP == res) ||
@@ -1354,12 +1447,14 @@ do_read_capacity(int sg_fd, struct opts_t *op)
pr2serr("Unable to calculate block size\n");
return (res > 0) ? res : SG_LIB_FILE_ERROR;
}
- } else if (vb) {
- strcpy(b,"OS error");
- if (res > 0)
- sg_get_category_sense_str(res, sizeof(b), b, vb);
- pr2serr("Read capacity(16): %s\n", b);
- pr2serr("Unable to calculate block size\n");
+ } else {
+ if (vb) {
+ strcpy(b,"OS error");
+ if (res > 0)
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("Read capacity(16): %s\n", b);
+ pr2serr("Unable to calculate block size\n");
+ }
return (res > 0) ? res : SG_LIB_FILE_ERROR;
}
op->bs_pi_do = op->expect_pi_do ? (op->bs + 8) : op->bs;
@@ -1374,9 +1469,10 @@ static const char * const opt_long_ctl_str =
* returns WANT_ZERO_EXIT so upper level yields an exist status of zero.
* Other return values (mainly SG_LIB_SYNTAX_ERROR) indicate errors. */
static int
-cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
- const char ** num_opp)
+parse_cmd_line(struct opts_t *op, int argc, char *argv[],
+ const char ** lba_opp, const char ** num_opp)
{
+ bool fail_if_strict = false;
int c, j;
int64_t ll;
const char * cp;
@@ -1421,8 +1517,38 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
pr2serr("bad argument to '--bs='. Expect 0 or greater\n");
return SG_LIB_SYNTAX_ERROR;
}
- op->bs = (uint32_t)j;
- op->bs_pi_do = op->bs;
+ if (j > 0) {
+ int k;
+ int m = j;
+ int highest_ind;
+
+ if (j < 512) {
+ pr2serr("warning: --bs=BS value is < 512 which seems too "
+ "small, continue\n");
+ fail_if_strict = true;
+ }
+ if (0 != (j % 8)) {
+ pr2serr("warning: --bs=BS value is not a multiple of 8, "
+ "unexpected, continue\n");
+ fail_if_strict = true;
+ }
+ for (k = 0, highest_ind = 0; k < 28; ++ k, m >>= 1) {
+ if (1 & m)
+ highest_ind = k;
+ } /* loop should get log_base2(j) */
+ k = 1 << highest_ind;
+ if (j == k) { /* j is a power of two; actual and logical
+ * block size is assumed to be the same */
+ op->bs = (uint32_t)j;
+ op->bs_pi_do = op->bs;
+ } else { /* j is not power_of_two, use as actual LB size */
+ op->bs = (uint32_t)k; /* power_of_two less than j */
+ op->bs_pi_do = (uint32_t)j;
+ }
+ } else { /* j==0, let READCAP sort this out */
+ op->bs = 0;
+ op->bs_pi_do = 0;
+ }
break;
case 'B': /* --bmop=OP,PGP (for ORWRITE(32)) */
j = sg_get_num(optarg);
@@ -1577,7 +1703,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
op->ref_tag = (uint32_t)ll;
break;
case 's':
- op->strict = true;
+ ++op->strict;
break;
case 'S':
j = sg_get_num(optarg);
@@ -1586,7 +1712,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
"inclusive\n");
return SG_LIB_SYNTAX_ERROR;
}
- op->scat_num_lbrd = (uint16_t)j;
+ op->scat_num_lbard = (uint16_t)j;
op->do_scattered = true;
op->cmd_name = "Write scattered";
break;
@@ -1634,7 +1760,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
op->expect_pi_do = (op->wrprotect > 0);
break;
case 'x':
- op->do_dry_run = true;
+ ++op->dry_run;
break;
default:
pr2serr("unrecognised option code 0x%x ??\n", c);
@@ -1654,28 +1780,330 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp,
return SG_LIB_SYNTAX_ERROR;
}
}
+ if (op->strict && fail_if_strict)
+ return SG_LIB_SYNTAX_ERROR;
return 0;
}
+static int
+process_scattered(int sg_fd, int infd, uint32_t if_len, uint32_t if_rlen,
+ int sfr_fd, uint32_t sf_len, uint64_t * addr_arr,
+ uint32_t addr_arr_len, uint32_t * num_arr,
+ uint16_t num_lbard, uint32_t sum_num, struct opts_t * op)
+{
+ int k, n, ret;
+ int vb = op->verbose;
+ uint32_t d, dd, nn, do_len;
+ uint8_t * up = NULL;
+ uint8_t * free_up = NULL;
+ char b[80];
+
+ if (op->do_combined) { /* --combined=DOF (.scat_lbdof) */
+ if (op->scat_lbdof > 0)
+ d = op->scat_lbdof * op->bs_pi_do;
+ else if (op->scat_num_lbard > 0) {
+ d = lbard_sz * (1 + op->scat_num_lbard);
+ if (0 != (d % op->bs_pi_do))
+ d = ((d / op->bs_pi_do) + 1) * op->bs_pi_do;
+ } else if (if_len > 0) {
+ d = if_len;
+ if (0 != (d % op->bs_pi_do))
+ d = ((d / op->bs_pi_do) + 1) * op->bs_pi_do;
+ } else {
+ pr2serr("With --combined= if DOF, RD are 0 and IF has an "
+ "unknown length\nthen give up\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ up = (uint8_t *)sg_memalign(d, sg_get_page_size(), &free_up, vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate aligned memory for "
+ "scatterlist+data\n");
+ return SG_LIB_OS_BASE_ERR + ENOMEM;
+ }
+ ret = bin_read(infd, up, ((if_len < d) ? if_len : d), "IF c1");
+ if (ret)
+ goto finii;
+ if (! check_lbrds(up, d, op, &num_lbard, &sum_num))
+ goto file_err_outt;
+ if ((op->scat_num_lbard > 0) && (op->scat_num_lbard != num_lbard)) {
+ bool rd_gt = (op->scat_num_lbard > num_lbard);
+
+ if (rd_gt || op->strict || vb) {
+ pr2serr("RD (%u) %s number of %ss (%u) found in SF\n",
+ op->scat_num_lbard, (rd_gt ? ">" : "<"), lbard_str,
+ num_lbard);
+ if (rd_gt)
+ goto file_err_outt;
+ else if (op->strict)
+ goto file_err_outt;
+ }
+ num_lbard = op->scat_num_lbard;
+ sum_num = sum_num_lbards(up, op->scat_num_lbard);
+ }
+ dd = lbard_sz * (num_lbard + 1);
+ if (0 != (dd % op->bs_pi_do))
+ dd = ((dd / op->bs_pi_do) + 1) * op->bs_pi_do; /* round up */
+ dd += (sum_num * op->bs_pi_do);
+ if (dd > d) {
+ uint8_t * u2p;
+ uint8_t * free_u2p;
+
+ u2p = (uint8_t *)sg_memalign(dd, sg_get_page_size(), &free_u2p,
+ vb > 4);
+ if (NULL == u2p) {
+ pr2serr("unable to allocate memory for final "
+ "scatterlist+data\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto finii;
+ }
+ memcpy(u2p, up, d);
+ free(free_up);
+ up = u2p;
+ free_up = free_u2p;
+ ret = bin_read(infd, up + d, dd - d, "IF c2");
+ if (ret)
+ goto finii;
+ }
+ do_len = dd;
+ op->numblocks = sum_num;
+ op->xfer_bytes = sum_num * op->bs_pi_do;
+ goto do_io;
+ }
+
+ /* other than do_combined, so --scat-file= or --lba= */
+ if (addr_arr_len > 0)
+ num_lbard = addr_arr_len;
+
+ if (op->scat_filename && (! op->do_scat_raw)) {
+ d = lbard_sz * (num_lbard + 1);
+ nn = d;
+ op->scat_lbdof = d / op->bs_pi_do;
+ if (0 != (d % op->bs_pi_do)) /* if not multiple, round up */
+ op->scat_lbdof += 1;
+ dd = op->scat_lbdof * op->bs_pi_do;
+ d = sum_num * op->bs_pi_do;
+ do_len = dd + d;
+ /* zeroed data-out buffer for SL+DATA */
+ up = (uint8_t *)sg_memalign(do_len, sg_get_page_size(), &free_up,
+ vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate aligned memory for "
+ "scatterlist+data\n");
+ return SG_LIB_OS_BASE_ERR + ENOMEM;
+ }
+ num_lbard = 0;
+ sum_num = 0;
+ nn = (nn > lbard_sz) ? nn : (op->scat_lbdof * op->bs_pi_do);
+ ret = build_t10_scat(op->scat_filename, op->do_16, ! op->do_scattered,
+ up, &num_lbard, &sum_num, nn);
+ if (ret)
+ goto finii;
+ /* Calculate number of bytes to read from IF (place in 'd') */
+ d = sum_num * op->bs_pi_do;
+ if (op->if_dlen > d) {
+ if (op->strict || vb) {
+ pr2serr("DLEN > than bytes implied by sum of scatter "
+ "list NUMs (%u)\n", d);
+ if (vb > 1)
+ pr2serr(" num_lbard=%u, sum_num=%u actual_bs=%u",
+ num_lbard, sum_num, op->bs_pi_do);
+ if (op->strict)
+ goto file_err_outt;
+ }
+ } else if ((op->if_dlen > 0) && (op->if_dlen < d))
+ d = op->if_dlen;
+ if ((if_rlen > 0) && (if_rlen != d)) {
+ bool readable_lt = (if_rlen < d);
+
+ if (vb)
+ pr2serr("readable length (%u) of IF %s bytes implied by "
+ "sum of\nscatter list NUMs (%u) and DLEN\n",
+ (uint32_t)if_rlen,
+ readable_lt ? "<" : ">", d);
+ if (op->strict) {
+ if ((op->strict > 1) || (! readable_lt))
+ goto file_err_outt;
+ }
+ if (readable_lt)
+ d = if_rlen;
+ }
+ if (0 != (d % op->bs_pi_do)) {
+ if (vb || (op->strict > 1)) {
+ pr2serr("Calculated data-out length (0x%x) not a "
+ "multiple of BS (%u", d, op->bs);
+ if (op->bs != op->bs_pi_do)
+ pr2serr(" + %d(PI)", (int)op->bs_pi_do - (int)op->bs);
+ if (op->strict > 1) {
+ pr2serr(")\nexiting ...\n");
+ goto file_err_outt;
+ } else
+ pr2serr(")\nzero pad and continue ...\n");
+ }
+ }
+ ret = bin_read(infd, up + (op->scat_lbdof * op->bs_pi_do), d,
+ "IF");
+ if (ret)
+ goto finii;
+ do_len = ((op->scat_lbdof + sum_num) * op->bs_pi_do);
+ op->numblocks = sum_num;
+ op->xfer_bytes = sum_num * op->bs_pi_do;
+ /* dout for scattered write with ASCII scat_file ready */
+ } else if (op->do_scat_raw) {
+ bool if_len_gt = false;
+
+ /* guessing game for length of buffer */
+ if (op->scat_num_lbard > 0) {
+ dd = (op->scat_num_lbard + 1) * lbard_sz;
+ if (sf_len < dd) {
+ pr2serr("SF not long enough (%u bytes) to provide RD "
+ "(%u) %ss\n", sf_len, dd, lbard_str);
+ goto file_err_outt;
+ }
+ nn = dd / op->bs_pi_do;
+ if (0 != (dd % op->bs_pi_do))
+ nn +=1;
+ dd = nn * op->bs_pi_do;
+ } else
+ dd = op->bs_pi_do; /* guess */
+ if (if_len > 0) {
+ nn = if_len / op->bs_pi_do;
+ if (0 != (if_len % op->bs_pi_do))
+ nn += 1;
+ d = nn * op->bs_pi_do;
+ } else
+ d = op->bs_pi_do; /* guess one LB */
+ /* zero data-out buffer for SL+DATA */
+ nn = dd + d;
+ up = (uint8_t *)sg_memalign(nn, sg_get_page_size(), &free_up, vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate aligned memory for "
+ "scatterlist+data\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto finii;
+ }
+ ret = bin_read(sfr_fd, up, sf_len, "SF");
+ if (ret)
+ goto finii;
+ if (! check_lbrds(up, dd, op, &num_lbard, &sum_num))
+ goto file_err_outt;
+ if (num_lbard != op->scat_num_lbard) {
+ pr2serr("Try again with --scattered=%u\n", num_lbard);
+ goto file_err_outt;
+ }
+ if ((sum_num * op->bs_pi_do) > d) {
+ uint8_t * u2p;
+ uint8_t * free_u2p;
+
+ d = sum_num * op->bs_pi_do;
+ nn = dd + d;
+ u2p = (uint8_t *)sg_memalign(nn, sg_get_page_size(), &free_u2p,
+ vb > 4);
+ if (NULL == u2p) {
+ pr2serr("unable to allocate memory for final "
+ "scatterlist+data\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto finii;
+ }
+ memcpy(u2p, up, dd);
+ free(free_up);
+ up = u2p;
+ free_up = free_u2p;
+ }
+ if ((if_len != (nn - d)) && (op->strict || vb)) {
+ if_len_gt = (if_len > (nn - d));
+ pr2serr("IF length (%u) %s 'sum_num' bytes (%u), ", if_len,
+ (if_len_gt ? ">" : "<"), nn - d);
+ if (op->strict > 1) {
+ pr2serr("exiting (strict=%d)\n", op->strict);
+ goto file_err_outt;
+ } else
+ pr2serr("continuing ...\n");
+ }
+ ret = bin_read(infd, up + d, (if_len_gt ? nn - d : if_len), "IF");
+ if (ret)
+ goto finii;
+ do_len = (num_lbard + sum_num) * op->bs_pi_do;
+ op->numblocks = sum_num;
+ op->xfer_bytes = sum_num * op->bs_pi_do;
+ } else if (addr_arr_len > 0) { /* build RDs for --lba= --num= */
+ d = lbard_sz * (num_lbard + 1);
+ op->scat_lbdof = d / op->bs_pi_do;
+ if (0 != (d % op->bs_pi_do)) /* if not multiple, round up */
+ op->scat_lbdof += 1;
+ for (sum_num = 0, k = 0; k < (int)addr_arr_len; ++k)
+ sum_num += num_arr[k];
+ do_len = ((op->scat_lbdof + sum_num) * op->bs_pi_do);
+ up = (uint8_t *)sg_memalign(do_len, sg_get_page_size(), &free_up,
+ vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate aligned memory for "
+ "scatterlist+data\n");
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
+ goto finii;
+ }
+ for (n = lbard_sz, k = 0; k < (int)addr_arr_len; ++k,
+ n += lbard_sz) {
+ sg_put_unaligned_be64(addr_arr[k], up + n + 0);
+ sg_put_unaligned_be32(num_arr[k], up + n + 8);
+ if (op->do_32) {
+ if (0 == k) {
+ sg_put_unaligned_be32(op->ref_tag, up + n + 12);
+ sg_put_unaligned_be16(op->app_tag, up + n + 16);
+ sg_put_unaligned_be16(op->tag_mask, up + n + 18);
+ } else {
+ sg_put_unaligned_be32((uint32_t)DEF_RT, up + n + 12);
+ sg_put_unaligned_be16((uint16_t)DEF_AT, up + n + 16);
+ sg_put_unaligned_be16((uint16_t)DEF_TM, up + n + 18);
+ }
+ }
+ }
+ op->numblocks = sum_num;
+ } else {
+ pr2serr("How did we get here??\n");
+ goto syntax_err_outt;
+ }
+do_io:
+ ret = do_write_x(sg_fd, up, do_len, op);
+ if (ret) {
+ strcpy(b,"OS error");
+ if (ret > 0)
+ sg_get_category_sense_str(ret, sizeof(b), b, vb);
+ pr2serr("%s: %s\n", op->cdb_name, b);
+ }
+ goto finii;
+
+syntax_err_outt:
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto finii;
+file_err_outt:
+ ret = SG_LIB_FILE_ERROR;
+finii:
+ if (free_up)
+ free(free_up);
+ return ret;
+}
+
int
main(int argc, char * argv[])
{
bool got_stdin = false;
bool got_stat = false;
- int k, n, err, vb;
+ bool if_reg_file = false;
+ int n, err, vb;
int infd = -1;
int sg_fd = -1;
- int rsl_fd = -1;
+ int sfr_fd = -1;
int ret = -1;
- uint32_t addr_arr_len, num_arr_len, do_len, nn, s;
- uint32_t num_lbrd = 0;
+ uint32_t nn, addr_arr_len, num_arr_len; /* --lba= */
+ uint32_t do_len = 0;
+ uint16_t num_lbard = 0;
uint32_t if_len = 0;
+ uint32_t sf_len = 0;
+ uint32_t sum_num = 0;
ssize_t res;
- off_t if_tot_len = 0;
+ off_t if_readable_len = 0;
struct opts_t * op;
- uint8_t * wBuff = NULL;
- uint8_t * free_wBuff = NULL;
const char * lba_op = NULL;
const char * num_op = NULL;
uint8_t * up = NULL;
@@ -1699,7 +2127,7 @@ main(int argc, char * argv[])
op->timeout = DEF_TIMEOUT_SECS;
/* Process command line */
- ret = cl_process(op, argc, argv, &lba_op, &num_op);
+ ret = parse_cmd_line(op, argc, argv, &lba_op, &num_op);
if (ret) {
if (WANT_ZERO_EXIT == ret)
return 0;
@@ -1740,27 +2168,59 @@ main(int argc, char * argv[])
"only with\nWRITE SCATTERED command)\n");
return SG_LIB_SYNTAX_ERROR;
}
+ if (op->scat_filename) {
+ pr2serr("Ambiguous: got --combined=DOF and --scat-file=SF .\n"
+ "Give one, the other or neither\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
if (lba_op || num_op) {
pr2serr("--scattered=RD --combined=DOF does not use --lba= or "
"--num=\nPlease remove.\n");
return SG_LIB_SYNTAX_ERROR;
}
+ if (op->do_scat_raw) {
+ pr2serr("Ambiguous: don't expect --combined=DOF and --scat-raw\n"
+ "Give one or the other\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
}
+ if ((NULL == op->scat_filename) && op->do_scat_raw) {
+ pr2serr("--scat-raw only applies to the --scat-file=SF option\n"
+ "--scat-raw without the --scat-file=SF option is an "
+ "error\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ n = (!! op->scat_filename) + (!! (lba_op || num_op)) +
+ (!! op->do_combined);
+ if (n > 1) {
+ pr2serr("want one and only one of: (--lba=LBA and/or --num=NUM), or\n"
+ "--scat-file=SF, or --combined=DOF\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (op->scat_filename && (1 == strlen(op->scat_filename)) &&
+ ('-' == op->scat_filename[0])) {
+ pr2serr("don't accept '-' (implying stdin) as a filename in "
+ "--scat-file=SF\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (vb && op->do_16 && (! is_pi_default(op)))
+ pr2serr("--app-tag=, --ref-tag= and --tag-mask= options ignored "
+ "with 16 byte commands\n");
/* examine .if_name . Open, move to .if_offset, calculate length that we
* want to read. */
- if (! op->ndob) {
- if_len = op->if_dlen;
+ if (! op->ndob) { /* as long as --same=1 is not active */
+ if_len = op->if_dlen; /* from --offset=OFF,DLEN; defaults to 0 */
if (NULL == op->if_name) {
pr2serr("Need --if=FN option to be given, exiting.\n");
if (vb > 1)
pr2serr("To write zeros use --in=/dev/zero\n");
+ pr2serr("\n");
usage((op->help > 0) ? op->help : 0);
return SG_LIB_SYNTAX_ERROR;
}
- if ((1 == strlen(op->if_name)) && ('-' == op->if_name[0]))
+ if ((1 == strlen(op->if_name)) && ('-' == op->if_name[0])) {
got_stdin = true;
- if (got_stdin) {
infd = STDIN_FILENO;
if (sg_set_binary_mode(STDIN_FILENO) < 0) {
perror("sg_set_binary_mode");
@@ -1772,84 +2232,73 @@ main(int argc, char * argv[])
op->if_name);
perror(ebuff);
return SG_LIB_FILE_ERROR;
- } else if (sg_set_binary_mode(infd) < 0)
+ }
+ if (sg_set_binary_mode(infd) < 0) {
perror("sg_set_binary_mode");
+ return SG_LIB_FILE_ERROR;
+ }
if (fstat(infd, &if_stat) < 0) {
snprintf(ebuff, EBUFF_SZ, "could not fstat %s", op->if_name);
perror(ebuff);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ goto file_err_out;
}
got_stat = true;
- if (S_ISREG(if_stat.st_mode))
- if_tot_len = if_stat.st_size;
+ if (S_ISREG(if_stat.st_mode)) {
+ if_reg_file = true;
+ if_readable_len = if_stat.st_size;
+ if (0 == if_len)
+ if_len = if_readable_len;
+ }
}
- if (got_stat && if_tot_len &&
- ((int64_t)op->if_offset >= (if_tot_len - 1))) {
+ if (got_stat && if_readable_len &&
+ ((int64_t)op->if_offset >= (if_readable_len - 1))) {
pr2serr("Offset (%" PRIu64 ") is at or beyond IF byte length (%"
- PRIu64 ")\n", op->if_offset, if_tot_len);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ PRIu64 ")\n", op->if_offset, (uint64_t)if_readable_len);
+ goto file_err_out;
}
if (op->if_offset > 0) {
off_t off = op->if_offset;
+ off_t h = if_readable_len;
- if (got_stdin) {
- if (vb)
- pr2serr("--offset= ignored when IF is stdin\n");
- } else {
- /* lseek() won't work with stdin or pipes, for example */
+ if (if_reg_file) {
+ /* lseek() won't work with stdin, pipes or sockets, etc */
if (lseek(infd, off, SEEK_SET) < 0) {
- snprintf(ebuff, EBUFF_SZ,
- "couldn't offset to required position on %s",
- op->if_name);
+ snprintf(ebuff, EBUFF_SZ, "couldn't offset to required "
+ "position on %s", op->if_name);
perror(ebuff);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ goto file_err_out;
}
- if_tot_len -= op->if_offset;
- if (if_tot_len <= 0) {
+ if_readable_len -= op->if_offset;
+ if (if_readable_len <= 0) {
pr2serr("--offset [0x%" PRIx64 "] at or beyond file "
"length[0x%" PRIx64 "]\n",
- (uint64_t)op->if_offset, (uint64_t)if_tot_len);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ (uint64_t)op->if_offset, (uint64_t)h);
+ goto file_err_out;
}
- if_len = (uint32_t)((if_tot_len < (off_t)op->if_dlen) ?
- if_tot_len : (off_t)op->if_dlen);
- }
- }
- if (0 != (if_len % op->bs_pi_do)) {
- if (op->strict) {
- pr2serr("Error: number of bytes to read from IF [%u] is "
- "not a multiple\nblock size %u (including"
- "protection information\n", (unsigned int)if_len,
- op->bs_pi_do);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ if (op->strict && ((off_t)op->if_dlen > if_readable_len)) {
+ pr2serr("after accounting for OFF, DLEN exceeds %s "
+ "remaining length (%u bytes)\n",
+ op->if_name, (uint32_t)if_readable_len);
+ goto file_err_out;
+ }
+ if_len = (uint32_t)((if_readable_len < (off_t)if_len) ?
+ if_readable_len : (off_t)if_len);
+ if (vb > 2)
+ pr2serr("Moved IF byte pointer to %u, if_len=%u, "
+ "if_readable_len=%u\n", (uint32_t)op->if_offset,
+ if_len, (uint32_t)if_readable_len);
+ } else {
+ if (vb)
+ pr2serr("--offset=OFF ignored when IF is stdin, pipe, "
+ "socket, etc\nDLEN, if given, is used\n");
}
- pr2serr("Warning: number of bytes to read from IF [%u] is not a "
- "multiple\nblock size %u (including protection "
- "information, if any);\npad with zeros",
- (unsigned int)if_len, op->bs_pi_do);
- if_len = (((unsigned int)if_len / op->bs_pi_do) + 1) *
- op->bs_pi_do; /* round up */
}
}
- /* A bit more sanity */
+ /* Check device name has been given */
if (NULL == op->device_name) {
pr2serr("missing device name!\n");
usage((op->help > 0) ? op->help : 0);
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- n = (!! op->scat_filename) + (!! (lba_op || num_op)) +
- (!! op->do_combined);
- if (1 != n) {
- pr2serr("want one and only one of: (--lba=LBA or --num=NUM), or "
- "--scat-file=SF,\nor --combined=DOF\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
+ goto syntax_err_out;
}
/* Open device file, do READ CAPACITY(16, maybe 10) if no BS */
@@ -1857,406 +2306,219 @@ main(int argc, char * argv[])
if (sg_fd < 0) {
pr2serr("open error: %s: %s\n", op->device_name,
safe_strerror(-sg_fd));
- return SG_LIB_FILE_ERROR;
+ goto file_err_out;
}
if (0 == op->bs) { /* ask DEVICE about logical/actual block size */
ret = do_read_capacity(sg_fd, op);
if (ret)
goto err_out;
}
+ if ((0 == op->bs_pi_do) || (0 == op->bs)) {
+ pr2serr("Logic error, need block size by now\n");
+ goto syntax_err_out;
+ }
+ if (! op->ndob) {
+ if (0 != (if_len % op->bs_pi_do)) {
+ if (op->strict > 1) {
+ pr2serr("Error: number of bytes to read from IF [%u] is "
+ "not a multiple\nblock size %u (including "
+ "protection information)\n", (unsigned int)if_len,
+ op->bs_pi_do);
+ goto file_err_out;
+ }
+ if (op->strict || vb)
+ pr2serr("Warning: number of bytes to read from IF [%u] is "
+ "not a multiple\nof actual block size %u; pad with "
+ "zeros\n", (unsigned int)if_len, op->bs_pi_do);
+ }
+ }
/* decode --lba= and --num= options */
memset(addr_arr, 0, sizeof(addr_arr));
memset(num_arr, 0, sizeof(num_arr));
addr_arr_len = 0;
- if (lba_op && num_op) {
+ num_arr_len = 0;
+ if (lba_op) {
if (0 != build_lba_arr(lba_op, addr_arr, &addr_arr_len,
MAX_NUM_ADDR)) {
pr2serr("bad argument to '--lba'\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
+ goto syntax_err_out;
}
- if (addr_arr_len > 0)
- op->explicit_lba = true;
+ }
+ if (num_op) {
if (0 != build_num_arr(num_op, num_arr, &num_arr_len,
MAX_NUM_ADDR)) {
pr2serr("bad argument to '--num'\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- if ((addr_arr_len != num_arr_len) || (num_arr_len <= 0)) {
- pr2serr("need same number of arguments to '--lba=' "
- "and '--num=' options\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
+ goto syntax_err_out;
}
}
- if (op->do_scattered) {
- uint32_t d;
- uint32_t sum_num = 0;
-
- /* if WRITE SCATTERED check for --scat-file=SF, if so state(SF) */
- if (op->scat_filename) {
- if (op->do_combined) {
- pr2serr("Ambiguous: got --combined=DOF and --scat-file=SF "
- ".\nGive one, the other or neither\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- if (stat(op->scat_filename, &sf_stat) < 0) {
- err = errno;
- pr2serr("Unable to stat(%s) as SF: %s\n", op->scat_filename,
- safe_strerror(err));
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- }
- /* some WRITE SCATTERED sanity checks */
- if (op->do_combined && op->do_scat_raw) {
- pr2serr("Ambiguous: do expect --combined=DOF and --scat-raw\n"
- "Give one or the other\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- if ((NULL == op->scat_filename) && op->do_scat_raw) {
- pr2serr("--scat-raw only applies to the --scat-file=SF option\n"
- "--scat-raw without the --scat-file=SF option is an "
- "error\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- if ((addr_arr_len > 0) && (op->scat_num_lbrd > 0) &&
- (op->scat_num_lbrd < addr_arr_len)) {
- pr2serr("less LBA,NUM pairs (%d )than --scattered=%d\n",
- addr_arr_len, op->scat_num_lbrd);
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
+ if (((addr_arr_len > 1) && (addr_arr_len != num_arr_len)) ||
+ ((0 == addr_arr_len) && (num_arr_len > 1))) {
+ /* allow all combinations of 0 or 1 element --lba= with 0 or 1
+ * element --num=, otherwise this error ... */
+ pr2serr("need same number of arguments to '--lba=' and '--num=' "
+ "options\n");
+ goto syntax_err_out;
+ }
+ if ((0 == addr_arr_len) && (1 == num_arr_len)) {
+ if (num_arr[0] > 0) {
+ pr2serr("won't write %u blocks without an explicit --lba= "
+ "option\n", num_arr[0]);
+ goto syntax_err_out;
}
- num_lbrd = (addr_arr_len > 0) ? addr_arr_len : op->scat_num_lbrd;
- if (num_lbrd < 15)
- num_lbrd = 15; /* 32 byte leadin, 15 32 byte LRD = 512 bytes */
- if (op->do_combined) {
- goto skip_scat_build;
+ addr_arr_len = 1; /* allow --num=0 without --lba= since it is safe */
+ }
+ /* Everything can use a SF, except --same=1 (when op->ndob==true) */
+ if (op->scat_filename) {
+ if (stat(op->scat_filename, &sf_stat) < 0) {
+ err = errno;
+ pr2serr("Unable to stat(%s) as SF: %s\n", op->scat_filename,
+ safe_strerror(err));
+ goto file_err_out;
}
if (op->do_scat_raw) {
- if (S_ISREG(sf_stat.st_mode)) {
- do_len = sf_stat.st_size;
- d = do_len / 32;
- if (0 == d) {
- pr2serr("raw SF must be at least 32 bytes long (followed "
- "by first LBA range descriptor\n");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- if (sf_stat.st_size % 32)
- d += 1; /* round up, will zero pad unfinished RD */
- if (op->scat_num_lbrd) {
- if (op->scat_num_lbrd != (d - 1)) {
- pr2serr("Command line RD (%u) contradicts value "
- "calculated from raw SF (%u)\n",
- op->scat_num_lbrd, d - 1);
- if (op->scat_num_lbrd < (d - 1))
- d = op->scat_num_lbrd + 1;
- else {
- pr2serr("Command line RD greater than raw SF "
- "file length implies, exit\n");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- }
- }
- } else {
- pr2serr("--scat-file= --scat-raw wants regular file for "
- "length\n");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ if (! S_ISREG(sf_stat.st_mode)) {
+ pr2serr("Expect scatter file to be a regular file\n");
+ goto file_err_out;
}
- num_lbrd = d;
- }
-
- /* Calculations to work out initial dout length */
- do_len = (1 + num_lbrd) * 32;
- op->scat_lbdof = do_len / op->bs_pi_do;
- if (0 != (do_len % op->bs_pi_do)) { /* if not multiple, round up */
- op->scat_lbdof += 1;
- do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do;
- }
- if (if_len > 0) {
- do_len += (uint32_t)if_len;
- } else { /* IF is stdin, a pipe or a device (special) ... */
- op->xfer_bytes = _SC_PAGE_SIZE; /* ... so need length */
- if (op->bs_pi_do > ((uint32_t)op->xfer_bytes / 2))
- op->xfer_bytes = op->bs_pi_do * 3;
- else if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
- op->xfer_bytes *= 4;
- if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
- op->xfer_bytes *= 4;
- if (do_len >= ((uint32_t)op->xfer_bytes / 2)) {
- pr2serr("Giving up guessing big enough buffers, "
- "please use --offset=OFF,DLEN\n");
- ret = SG_LIB_SYNTAX_ERROR;
- goto err_out;
- }
- }
- }
- do_len = op->xfer_bytes;
- }
- if (0 != (do_len % op->bs_pi_do)) /* round up */
- do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do;
- if (do_len < op->bs_pi_do) {
- pr2serr("failed calculating data-out buffer size (%u)\n",
- do_len);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- d = do_len / op->bs_pi_do;
- if (0 != (do_len % op->bs_pi_do)) {
- d += 1;
- do_len = d * op->bs_pi_do;
- }
- op->xfer_bytes = do_len;
- up = sg_memalign(d * op->bs_pi_do, sg_get_page_size(), &free_up,
- op->verbose); /* zeroed data-out buffer for SL+DATA */
- if (NULL == up) {
- pr2serr("unable to allocate memory for scatterlist+data\n");
- ret = SG_LIB_OS_BASE_ERR + ENOMEM;
- goto err_out;
- }
-
- if (op->do_scat_raw) {
- rsl_fd = open(op->scat_filename, O_RDONLY);
- if (rsl_fd < 0) {
+ sf_len = sf_stat.st_size;
+ sfr_fd = open(op->scat_filename, O_RDONLY);
+ if (sfr_fd < 0) {
err = errno;
pr2serr("Failed to open %s for raw read: %s\n",
op->scat_filename, safe_strerror(err));
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ goto file_err_out;
}
- if (sg_set_binary_mode(rsl_fd) < 0) {
+ if (sg_set_binary_mode(sfr_fd) < 0) {
perror("sg_set_binary_mode");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- if (sf_stat.st_size < 32) {
- pr2serr("Logic error, how did this happen?\n");
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
+ goto file_err_out;
}
- res = read(rsl_fd, up, sf_stat.st_size);
- if (res < 0) {
- err = errno;
- pr2serr("Error doing raw read of SF file: %s\n",
- safe_strerror(err));
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- if (res < sf_stat.st_size) {
- pr2serr("Short (%u) raw read of SF file, wanted %" PRIu64
- "\n", (unsigned int)res, sf_stat.st_size);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- close(rsl_fd);
- rsl_fd = -1;
- } else if (op->scat_filename) {
- ret = build_t10_scat(op->scat_filename, op->do_16, false,
- up, &d, &num_lbrd, &sum_num,
- op->scat_lbdof * op->bs_pi_do);
+ } else { /* scat_file should contain ASCII hex, preliminary parse */
+ nn = (op->scat_num_lbard > 0) ?
+ lbard_sz * (1 + op->scat_num_lbard) : 0;
+ ret = build_t10_scat(op->scat_filename, op->do_16,
+ ! op->do_scattered, NULL, &num_lbard,
+ &sum_num, nn);
if (ret)
goto err_out;
- if (num_lbrd > 0)
- op->explicit_lba = true;
- op->numblocks = sum_num;
- if (vb > 1)
- pr2serr("After build_t10_scat(): num_lbrd=%u sum_num=%u\n",
- num_lbrd, sum_num);
- } else if (addr_arr_len > 0) { /* build RDs for --addr= --num= */
- for (n = 32, k = 0; k < (int)addr_arr_len; ++k, n += 32) {
- sg_put_unaligned_be64(addr_arr[k], up + n + 0);
- sg_put_unaligned_be32(num_arr[k], up + n + 8);
- sum_num += num_arr[k];
- if (op->do_32) {
- if (0 == k) {
- sg_put_unaligned_be32(op->ref_tag, up + n + 12);
- sg_put_unaligned_be16(op->app_tag, up + n + 16);
- sg_put_unaligned_be16(op->tag_mask, up + n + 18);
- } else {
- sg_put_unaligned_be32((uint32_t)DEF_RT, up + n + 12);
- sg_put_unaligned_be16((uint16_t)DEF_AT, up + n + 16);
- sg_put_unaligned_be16((uint16_t)DEF_TM, up + n + 18);
- }
- }
- }
- op->numblocks = sum_num;
- }
- /* now read data to write component into 'up' */
- d = op->scat_lbdof * op->bs_pi_do;
- if (d > (uint32_t)op->xfer_bytes) {
- pr2serr("Logic error in scattered, read data into buffer "
- "(d=%u)\n", d);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- s = op->scat_lbdof + op->numblocks;
- nn = s * op->bs_pi_do;
- if ((uint32_t)op->xfer_bytes < nn) {
- uint8_t * u2p;
- uint8_t * free_u2p;
+ if ((op->scat_num_lbard > 0) &&
+ (num_lbard != op->scat_num_lbard)) {
+ bool rd_gt = (op->scat_num_lbard > num_lbard);
- u2p = sg_memalign(nn, sg_get_page_size(), &free_u2p,
- op->verbose > 3);
- if (NULL == u2p) {
- pr2serr("unable to allocate memory for final "
- "scatterlist+data\n");
- ret = SG_LIB_OS_BASE_ERR + ENOMEM;
- goto err_out;
+ if (rd_gt || op->strict || vb) {
+ pr2serr("RD (%u) %s number of %ss (%u) found in SF\n",
+ op->scat_num_lbard, (rd_gt ? ">" : "<"),
+ lbard_str, num_lbard);
+ if (rd_gt)
+ goto file_err_out;
+ else if (op->strict)
+ goto file_err_out;
+ }
}
- memcpy(u2p, up, d);
- free(free_up);
- up = u2p;
- free_up = free_u2p;
- op->xfer_bytes = nn;
- }
- res = read(infd, up + d, op->xfer_bytes - d);
- d = op->xfer_bytes - d;
- if (res < 0) {
- err = errno;
- pr2serr("Error doing read of IF file: %s\n", safe_strerror(err));
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- if ((uint32_t)res < d) {
- pr2serr("Short (%u) read of IF file, wanted %u\n",
- (unsigned int)res, d);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
}
}
-skip_scat_build: // needs more XXXXXXXXX xxxxxx ???
- if (vb) {
- if (op->do_16 && (! is_pi_default(op)))
- pr2serr("--app-tag=, --ref-tag= and --tag-mask= options ignored "
- "with 16 byte commands\n");
+ if (op->do_scattered) {
+ ret = process_scattered(sg_fd, infd, if_len, if_readable_len, sfr_fd,
+ sf_len, addr_arr, addr_arr_len, num_arr,
+ num_lbard, sum_num, op);
+ goto fini;
}
- if (op->do_same)
- op->xfer_bytes = 1 * op->bs_pi_do;
- else if (op->do_scattered) {
- if (op->do_combined) {
- int up_len;
- uint32_t sum_num;
-
- if ((if_len < 32) || (op->bs_pi_do < 32)) {
- pr2serr("Logic error combined alloc should be > %u, "
- "bs_pi_do=%u\n", (uint32_t)if_len, op->bs_pi_do);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- /* assume if_len % op->bs_pi_do is zero (i.e. no remainder) */
- up = sg_memalign(if_len, sg_get_page_size(), &free_up,
- op->verbose > 3);
- if (NULL == up) {
- pr2serr("unable to allocate memory for combined\n");
- ret = SG_LIB_OS_BASE_ERR + ENOMEM;
- goto err_out;
- }
- up_len = (op->scat_lbdof > 0) ? (op->scat_lbdof * op->bs_pi_do) :
- if_len;
- num_lbrd = 0;
- sum_num = 0;
- if (! check_lbrds(up, up_len, op, &num_lbrd, &sum_num)) {
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- op->numblocks = sum_num;
- } else {
- ; /* already done, scatter_list+data waiting in 'up' */
- }
- } else
- op->xfer_bytes = op->numblocks * op->bs_pi_do;
- if (! op->do_scattered) {
- if (addr_arr_len > 0) {
- op->lba = addr_arr[0];
- op->numblocks = num_arr[0];
- } else if (op->scat_filename) {
- uint32_t dd = 0;
- uint32_t sum_num = 0;
- uint8_t upp[96];
-
- ret = build_t10_scat(op->scat_filename, op->do_16, true,
- upp, &dd, &num_lbrd, &sum_num,
- sizeof(upp));
- if (ret)
- goto err_out;
- if (op->verbose > 2)
- pr2serr("after build_t10_scat, num_lbrd=%u, sum_num=%u "
- "(dd=%u)\n", num_lbrd, sum_num, dd);
- if (1 != num_lbrd) {
- pr2serr("Unable to decode one LBA range descriptor from %s\n",
- op->scat_filename);
- ret = SG_LIB_FILE_ERROR;
- goto err_out;
- }
- op->lba = sg_get_unaligned_be64(upp + 32 + 0);
- op->numblocks = sg_get_unaligned_be32(upp + 32 + 8);
- if (! op->do_16) {
- op->ref_tag = sg_get_unaligned_be32(upp + 32 + 12);
- op->app_tag = sg_get_unaligned_be16(upp + 32 + 16);
- op->tag_mask = sg_get_unaligned_be16(upp + 32 + 18);
- }
- }
- }
+ /* other than scattered */
+ if (addr_arr_len > 0) {
+ op->lba = addr_arr[0];
+ op->numblocks = num_arr[0];
+ if (vb && (addr_arr_len > 1))
+ pr2serr("warning: %d LBA,number_of_blocks pairs found, only "
+ "taking first\n", addr_arr_len);
+ } else if (op->scat_filename && (! op->do_scat_raw)) {
+ uint32_t sum_num = 0;
+ uint8_t upp[96];
- if (op->ndob) {
-
- } else if (op->do_scattered)
- wBuff = up;
- else if (op->xfer_bytes > 0) {
- /* fill allocated buffer with zeros */
- wBuff = (uint8_t *)sg_memalign(op->numblocks * op->bs_pi_do,
- sg_get_page_size(), &free_wBuff,
- op->verbose > 3);
- if (NULL == wBuff) {
- pr2serr("unable to allocate %" PRId64 " bytes of memory\n",
- (int64_t)op->xfer_bytes);
- ret = SG_LIB_OS_BASE_ERR + ENOMEM;
- goto err_out;
- }
- res = read(infd, wBuff, op->xfer_bytes);
- if (res < 0) {
- snprintf(ebuff, EBUFF_SZ, "couldn't read from %s", op->if_name);
- perror(ebuff);
- ret = SG_LIB_FILE_ERROR;
+ ret = build_t10_scat(op->scat_filename, op->do_16,
+ true /* parse one */, upp, &num_lbard,
+ &sum_num, sizeof(upp));
+ if (ret)
goto err_out;
+ if (vb && (num_lbard > 1))
+ pr2serr("warning: %d LBA,number_of_blocks pairs found, only "
+ "taking first\n", num_lbard);
+ if (vb > 2)
+ pr2serr("after build_t10_scat, num_lbard=%u, sum_num=%u\n",
+ num_lbard, sum_num);
+ if (1 != num_lbard) {
+ pr2serr("Unable to decode one LBA range descriptor from %s\n",
+ op->scat_filename);
+ goto file_err_out;
+ }
+ op->lba = sg_get_unaligned_be64(upp + 32 + 0);
+ op->numblocks = sg_get_unaligned_be32(upp + 32 + 8);
+ if (op->do_32) {
+ op->ref_tag = sg_get_unaligned_be32(upp + 32 + 12);
+ op->app_tag = sg_get_unaligned_be16(upp + 32 + 16);
+ op->tag_mask = sg_get_unaligned_be16(upp + 32 + 18);
+ }
+ } else if (op->do_scat_raw) {
+ uint8_t upp[64];
+
+ if (sf_len < (2 * lbard_sz)) {
+ pr2serr("raw scatter file must be at least 64 bytes long "
+ "(length: %u)\n", sf_len);
+ goto file_err_out;
}
- if (op->strict && (res != op->xfer_bytes)) {
- if (vb)
- pr2serr("Wanted to read %" PRId64 " bytes but got %" PRId64
- " bytes and --strict given\n",
- (int64_t)op->xfer_bytes, (int64_t)res);
- ret = SG_LIB_FILE_ERROR;
+ ret = bin_read(sfr_fd, upp, sizeof(upp), "SF");
+ if (ret)
goto err_out;
- }
- } else if (op->xfer_bytes < 0) {
- pr2serr("Product of block size (%" PRIu32 ") and number of blocks "
- "(%" PRIu32 ") too\nlarge for single read\n", op->bs,
- op->numblocks);
- ret = SG_LIB_SYNTAX_ERROR;
+ if (! check_lbrds(upp, sizeof(upp), op, &num_lbard, &sum_num))
+ goto file_err_out;
+ if (1 != num_lbard) {
+ pr2serr("No %ss found in SF (num=%u)\n", lbard_str, num_lbard);
+ goto file_err_out;
+ }
+ op->lba = sg_get_unaligned_be64(upp + 16);
+ op->numblocks = sg_get_unaligned_be32(upp + 16 + 8);
+ do_len = sum_num * op->bs_pi_do;
+ op->xfer_bytes = do_len;
+ } else {
+ pr2serr("No LBA or number_of_blocks given, try using --lba= and "
+ "--num=\n");
+ goto syntax_err_out;
+ }
+ if (op->do_same)
+ op->xfer_bytes = op->ndob ? 0 : op->bs_pi_do;
+ else /* WRITE, ORWRITE, WRITE ATOMIC or WRITE STREAM */
+ op->xfer_bytes = op->numblocks * op->bs_pi_do;
+ do_len = op->xfer_bytes;
+
+ /* fill allocated buffer with zeros */
+ up = (uint8_t *)sg_memalign(do_len, sg_get_page_size(), &free_up,
+ vb > 4);
+ if (NULL == up) {
+ pr2serr("unable to allocate %u bytes of memory\n", do_len);
+ ret = SG_LIB_OS_BASE_ERR + ENOMEM;
goto err_out;
}
+ ret = bin_read(infd, up, ((if_len < do_len) ? if_len : do_len), "IF");
+ if (ret)
+ goto fini;
- ret = do_write_x(sg_fd, wBuff, op->xfer_bytes, op);
+ ret = do_write_x(sg_fd, up, do_len, op);
if (ret) {
strcpy(b,"OS error");
if (ret > 0)
sg_get_category_sense_str(ret, sizeof(b), b, vb);
pr2serr("%s: %s\n", op->cdb_name, b);
}
+ goto fini;
+syntax_err_out:
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+file_err_out:
+ ret = SG_LIB_FILE_ERROR;
err_out:
- if (free_wBuff)
- free(free_wBuff);
+fini:
if (free_up)
free(free_up);
if (sg_fd >= 0) {
@@ -2264,16 +2526,21 @@ err_out:
if (res < 0) {
pr2serr("sg_fd close error: %s\n", safe_strerror(-res));
if (0 == ret)
- return SG_LIB_FILE_ERROR;
+ ret = SG_LIB_FILE_ERROR;
+ }
+ }
+ if (sfr_fd >= 0) {
+ if (close(sfr_fd) < 0) {
+ perror("sfr_fd close error");
+ if (0 == ret)
+ ret = SG_LIB_FILE_ERROR;
}
}
- if (rsl_fd >= 0)
- close(rsl_fd);
if ((! got_stdin) && (infd >= 0)) {
if (close(infd) < 0) {
perror("infd close error");
if (0 == ret)
- return SG_LIB_FILE_ERROR;
+ ret = SG_LIB_FILE_ERROR;
}
}
return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;