diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2009-09-19 22:48:01 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2009-09-19 22:48:01 +0000 |
commit | 4edce602a56c510b39e80642a29153d13e8acfd4 (patch) | |
tree | d27e1f599cde3671cf65ac34c86526e30068151c /src/sg_get_lba_status.c | |
parent | 9e11e00479a4cd919028ecf226856e4c78c87354 (diff) | |
download | sg3_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.c | 350 |
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; +} |