aboutsummaryrefslogtreecommitdiff
path: root/src/sg_get_lba_status.c
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2009-09-19 22:48:01 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2009-09-19 22:48:01 +0000
commit4edce602a56c510b39e80642a29153d13e8acfd4 (patch)
treed27e1f599cde3671cf65ac34c86526e30068151c /src/sg_get_lba_status.c
parent9e11e00479a4cd919028ecf226856e4c78c87354 (diff)
downloadsg3_utils-4edce602a56c510b39e80642a29153d13e8acfd4.tar.gz
testing sg_get_lba_status
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@301 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src/sg_get_lba_status.c')
-rw-r--r--src/sg_get_lba_status.c350
1 files changed, 350 insertions, 0 deletions
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;
+}