aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rw-r--r--doc/Makefile.am13
-rw-r--r--doc/sg_get_lba_status.857
-rw-r--r--src/Makefile.am17
-rw-r--r--src/sg_get_lba_status.c350
5 files changed, 426 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 0b57ce19..ce3fa644 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,9 +2,10 @@ 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.28 [20090918] [svn: r299]
+Changelog for sg3_utils-1.28 [20090920] [svn: r301]
- sg_unmap: new utility for thin provisioning
- add examples/sg_unmap_example.txt
+ - sg_get_lba_status: new utility for thin provisioning
- sg_read_block_limits: new utility for tape drives
- sg_logs: add cache memory statistics log (sub)page
- sg_vpd, sg_inq: extend Block limits VPD page (sbc3r19)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 5666ea87..8f73d838 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -9,7 +9,8 @@ if OS_LINUX
man_MANS = \
sg3_utils.8 \
- sg_dd.8 sg_emc_trespass.8 sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_dd.8 sg_emc_trespass.8 sg_format.8 sg_get_config.8 \
+ sg_get_lba_status.8 sg_ident.8 \
sginfo.8 sg_inq.8 sg_logs.8 sg_luns.8 sg_map26.8 sg_map.8 \
sgm_dd.8 sg_modes.8 sg_opcodes.8 sgp_dd.8 sg_persist.8 \
sg_prevent.8 sg_raw.8 sg_rbuf.8 sg_rdac.8 sg_read.8 sg_readcap.8 \
@@ -33,7 +34,7 @@ if OS_WIN32_MINGW
man_MANS = \
sg3_utils.8 \
- sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_format.8 sg_get_config.8 sg_get_lba_status.8 sg_ident.8 \
sg_inq.8 sg_logs.8 sg_luns.8 \
sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \
sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \
@@ -56,7 +57,7 @@ if OS_WIN32_CYGWIN
man_MANS = \
sg3_utils.8 \
- sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_format.8 sg_get_config.8 sg_get_lba_status.8 sg_ident.8 \
sg_inq.8 sg_logs.8 sg_luns.8 \
sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \
sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \
@@ -79,7 +80,7 @@ if OS_FREEBSD
man_MANS = \
sg3_utils.8 \
- sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_format.8 sg_get_config.8 sg_get_lba_status.8 sg_ident.8 \
sg_inq.8 sg_logs.8 sg_luns.8 \
sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \
sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \
@@ -96,7 +97,7 @@ if OS_SOLARIS
man_MANS = \
sg3_utils.8 \
- sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_format.8 sg_get_config.8 sg_get_lba_status.8 sg_ident.8 \
sg_inq.8 sg_logs.8 sg_luns.8 \
sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \
sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \
@@ -113,7 +114,7 @@ if OS_OSF
man_MANS = \
sg3_utils.8 \
- sg_format.8 sg_get_config.8 sg_ident.8 \
+ sg_format.8 sg_get_config.8 sg_get_lba_status.8 sg_ident.8 \
sg_inq.8 sg_logs.8 sg_luns.8 \
sg_modes.8 sg_opcodes.8 sg_persist.8 sg_prevent.8 sg_raw.8 \
sg_rdac.8 sg_readcap.8 sg_read_block_limits.8 sg_read_buffer.8 \
diff --git a/doc/sg_get_lba_status.8 b/doc/sg_get_lba_status.8
new file mode 100644
index 00000000..5969ba5e
--- /dev/null
+++ b/doc/sg_get_lba_status.8
@@ -0,0 +1,57 @@
+.TH SG_GET_LBA_STATUS "8" "September 2009" "sg3_utils\-1.28" SG3_UTILS
+.SH NAME
+sg_luns \- send the SCSI GET LBA STATUS command
+.SH SYNOPSIS
+.B sg_get_lba_status
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send the SCSI GET LBA STATUS command to the \fIDEVICE\fR and outputs the
+response. This command was introduced in (draft) SBC-3 revision 20 and
+devices that support thin provisioning should support this command.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response to this command in ASCII hex.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the starting logical block address (lba) to check the
+status for.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
+then 24 is used.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+output ASCII hex rendering of each report lun, one per line.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary (to stdout).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_luns is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_write_same(8), sg_unmap(8)
diff --git a/src/Makefile.am b/src/Makefile.am
index 1de843aa..643acd65 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,8 +8,8 @@ if OS_LINUX
# sg_scan is shared by Linux and Win32
bin_PROGRAMS = \
- sg_dd sg_emc_trespass sg_format sg_get_config sg_ident \
- sginfo sg_inq sg_logs sg_luns sg_map26 sg_map \
+ sg_dd sg_emc_trespass sg_format sg_get_config sg_get_lba_status \
+ sg_ident sginfo sg_inq sg_logs sg_luns sg_map26 sg_map \
sgm_dd sg_modes sg_opcodes sgp_dd sg_persist \
sg_prevent sg_raw sg_rbuf sg_rdac sg_read sg_readcap \
sg_read_block_limits sg_read_buffer sg_read_long sg_reassign \
@@ -30,7 +30,7 @@ endif
if OS_WIN32_MINGW
bin_PROGRAMS = \
- sg_format sg_get_config sg_ident \
+ sg_format sg_get_config sg_get_lba_status sg_ident \
sg_inq sg_logs sg_luns \
sg_modes sg_opcodes sg_persist \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
@@ -52,7 +52,7 @@ endif
if OS_WIN32_CYGWIN
bin_PROGRAMS = \
- sg_format sg_get_config sg_ident \
+ sg_format sg_get_config sg_get_lba_status sg_ident \
sg_inq sg_logs sg_luns \
sg_modes sg_opcodes sg_persist \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
@@ -74,7 +74,7 @@ endif
if OS_FREEBSD
bin_PROGRAMS = \
- sg_format sg_get_config sg_ident \
+ sg_format sg_get_config sg_get_lba_status sg_ident \
sg_inq sg_logs sg_luns \
sg_modes sg_opcodes sg_persist \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
@@ -90,7 +90,7 @@ endif
if OS_SOLARIS
bin_PROGRAMS = \
- sg_format sg_get_config sg_ident \
+ sg_format sg_get_config sg_get_lba_status sg_ident \
sg_inq sg_logs sg_luns \
sg_modes sg_opcodes sg_persist \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
@@ -106,7 +106,7 @@ endif
if OS_OSF
bin_PROGRAMS = \
- sg_format sg_get_config sg_ident \
+ sg_format sg_get_config sg_get_lba_status sg_ident \
sg_inq sg_logs sg_luns \
sg_modes sg_opcodes sg_persist \
sg_prevent sg_raw sg_rdac sg_readcap sg_read_block_limits \
@@ -134,6 +134,9 @@ sg_format_LDADD = ../lib/libsgutils2.la @os_libs@
sg_get_config_SOURCES = sg_get_config.c
sg_get_config_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_get_lba_status_SOURCES = sg_get_lba_status.c
+sg_get_lba_status_LDADD = ../lib/libsgutils2.la @os_libs@
+
sg_ident_SOURCES = sg_ident.c
sg_ident_LDADD = ../lib/libsgutils2.la @os_libs@
diff --git a/src/sg_get_lba_status.c b/src/sg_get_lba_status.c
new file mode 100644
index 00000000..574a0bc6
--- /dev/null
+++ b/src/sg_get_lba_status.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2009 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI GET LBA STATUS command to the given SCSI device.
+ */
+
+static char * version_str = "1.00 20090920";
+
+#define MAX_GLBAS_BUFF_LEN (1024 * 1024)
+#define DEF_GLBAS_BUFF_LEN 24
+
+static unsigned char glbasBuff[DEF_GLBAS_BUFF_LEN];
+static unsigned char * glbasBuffp = glbasBuff;
+
+
+static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"lba", required_argument, 0, 'l'},
+ {"maxlen", required_argument, 0, 'm'},
+ {"quiet", no_argument, 0, 'q'},
+ {"raw", no_argument, 0, 'r'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: "
+ "sg_get_lba_status [--help] [--hex] [--lba=LBA] [--maxlen=LEN] "
+ "[--quiet]\n"
+ " [--raw] [--verbose] [--version] DEVICE\n"
+ " where:\n"
+ " --help|-h print out usage message\n"
+ " --hex|-H output in hexadecimal\n"
+ " --lba=LBA|-l LBA starting LBA (logical block address) "
+ "(def: 0)\n"
+ " --maxlen=LEN|-m LEN max response length (allocation "
+ "length in cdb)\n"
+ " (def: 0 -> %d bytes)\n",
+ DEF_RLUNS_BUFF_LEN );
+ fprintf(stderr, " --quiet|-q output 1 if LBA mapped, 0 "
+ "if unmapped\n"
+ " --raw|-r output in binary\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n\n"
+ "Performs a SCSI GET LBA STATUS command (SBC-3)\n"
+ );
+
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+ int k;
+
+ for (k = 0 ; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+/* Decodes given LBA status descriptor passing back the starting LBA,
+ * the number of blocks and returns the provisioning status, -1 for error.
+ */
+static int
+decode_lba_status_desc(const unsigned char * ucp, uint64_t * slbap,
+ uint32_t * blocksp)
+{
+ int j;
+ uint32_t blocks;
+ uint64_t ull;
+
+ if (NULL == ucp)
+ return -1;
+ ull = 0;
+ for (j = 0; j < 8; ++j) {
+ if (j > 0)
+ ull <<= 8;
+ ull |= ucp[j];
+ }
+ blocks = 0;
+ for (j = 0; j < 4; ++j) {
+ if (j > 0)
+ blocks <<= 8;
+ blocks |= ucp[8 + j];
+ }
+ if (slbap)
+ *slbap = ull;
+ if (blocksp)
+ *blocksp = blocks;
+ return ucp[12] & 0xf;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ int sg_fd, k, j, res, c, num_descs;
+ int do_hex = 0;
+ int64_t ll;
+ uint64_t lba = 0;
+ uint64_t d_lba;
+ uint32_t d_blocks;
+ int maxlen = DEF_GLBAS_BUFF_LEN;
+ int do_quiet = 0;
+ int do_raw = 0;
+ int verbose = 0;
+ const char * device_name = NULL;
+ const unsigned char * ucp;
+ int ret = 0;
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "hHl:m:qrvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ ++do_hex;
+ break;
+ case 'l':
+ ll = sg_get_llnum(optarg);
+ if (-1 == ll) {
+ fprintf(stderr, "bad argument to '--lba'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ lba = (uint64_t)ll;
+ break;
+ case 'm':
+ maxlen = sg_get_num(optarg);
+ if ((maxlen < 0) || (maxlen > MAX_GLBAS_BUFF_LEN)) {
+ fprintf(stderr, "argument to '--maxlen' should be %d or "
+ "less\n", MAX_GLBAS_BUFF_LEN);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'q':
+ ++do_quiet;
+ break;
+ case 'r':
+ ++do_raw;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ fprintf(stderr, "version: %s\n", version_str);
+ return 0;
+ default:
+ fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == device_name) {
+ device_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ fprintf(stderr, "Unexpected extra argument: %s\n",
+ argv[optind]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+
+ if (NULL == device_name) {
+ fprintf(stderr, "missing device name!\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (maxlen > DEF_GLBAS_BUFF_LEN) {
+ glbasBuffp = (unsigned char *)calloc(max_len, 1);
+ if (NULL == glbasBuffp) {
+ fprintf(stderr, "unable to allocate %d bytes on heap\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ ret = SG_LIB_FILE_ERROR;
+ goto free_buff;
+ }
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+ if (sg_fd < 0) {
+ fprintf(stderr, "open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ ret = SG_LIB_FILE_ERROR;
+ goto free_buff;
+ }
+
+ res = sg_ll_get_lba_status(sg_fd, lba, glbasBuffp, maxlen, 1,
+ verbose);
+ ret = res;
+ if (0 == res) {
+ if (do_raw) {
+ dStrRaw((const char *)glbasBuffp, maxlen);
+ goto the_end;
+ }
+ if (do_hex) {
+ dStrHex((const char *)glbasBuffp, maxlen, 1);
+ goto the_end;
+ }
+ if (maxlen < 4) {
+ if (verbose)
+ fprintf(stderr, "Exiting because allocation length (maxlen) "
+ " less than 4\n");
+ goto the_end;
+ }
+ rlen = (glbasBuffp[0] << 24) + (glbasBuffp[1] << 16) +
+ (glbasBuffp[2] << 8) + glbasBuffp[3] + 4;
+ if ((verbose > 1) || (verbose && (rlen > maxlen))) {
+ fprintf(stderr, "response length %d bytes\n", rlen);
+ if (rlen > maxlen)
+ fprintf(stderr, " ... which is greater than maxlen "
+ "(allocation length %d), truncation\n", rlen, maxlen);
+ }
+ if (rlen > maxlen)
+ rlen = maxlen;
+
+ if (do_quiet) {
+ if (rlen < 24) {
+ fprintf(stderr, "Need maxlen and response length to "
+ " be at least 24, have %d bytes\n", rlen);
+ ret = SG_LIB_CAT_OTHER;
+ goto the_end;
+ }
+ res = decode_lba_status_desc(glbasBuffp + 8, &d_lba, &d_blocks);
+ if ((res < 0) || (res > 15)) {
+ fprintf(stderr, "first LBA status descriptor returned %d "
+ "??\n", res);
+ ret = SG_LIB_CAT_OTHER;
+ goto the_end;
+ }
+ if ((lba < d_lba) || (lba >= (d_lba + d_blocks))) {
+ fprintf(stderr, "given LBA not range of first descriptor\n"
+ "descriptor LBA: 0x");
+ for (j = 0; j < 8; ++j)
+ fprintf(stderr, "%02x", glbasBuffp[8 + j]);
+ fprintf(stderr, " blocks: 0x%x\n", d_blocks);
+ ret = SG_LIB_CAT_OTHER;
+ goto the_end;
+ }
+ printf("%d\n", res);
+ goto the_end;
+ }
+
+ if (rlen < 24) {
+ printf("No complete LBA status descriptors available\n");
+ goto the_end;
+ }
+ num_descs = (rlen - 8) / 16;
+ if (verbose)
+ fprintf(stderr, "%d complete LBA status descriptors found\n",
+ num_descs);
+ for (ucp = glbasBuffp + 8, k = 0; k < num_descs; ucp += 16, ++k) {
+ res = decode_lba_status_desc(ucp, &d_lba, &d_blocks);
+ if ((res < 0) || (res > 15))
+ fprintf(stderr, "descriptor %d: bad LBA status descriptor "
+ "returned %d\n", k + 1, res);
+ printf("descriptor LBA: 0x");
+ for (j = 0; j < 8; ++j)
+ printf("%02x", ucp[j]);
+ printf(" blocks: %d\n", d_blocks);
+ }
+ if ((num_descs * 16) + 8 < rlen)
+ fprintf(stderr, "incomplete trailing LBA status descriptors "
+ "found\n");
+ } else if (SG_LIB_CAT_INVALID_OP == res)
+ fprintf(stderr, "Get LBA Status command not supported\n");
+ else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+ fprintf(stderr, "Get LBA Status, aborted command\n");
+ else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+ fprintf(stderr, "Get LBA Status command has bad field in cdb\n");
+ else {
+ fprintf(stderr, "Get LBA Status command failed\n");
+ if (0 == verbose)
+ fprintf(stderr, " try '-v' option for more information\n");
+ }
+
+the_end:
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = SG_LIB_FILE_ERROR;
+ }
+free_buff:
+ if (glbasBuffp && (glbasBuffp != glbasBuff))
+ free(glbasBuffp);
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}