1 /*
2  * Copyright (c) 2009-2015 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  */
7 
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <getopt.h>
14 #define __STDC_FORMAT_MACROS 1
15 #include <inttypes.h>
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 #include "sg_lib.h"
21 #include "sg_cmds_basic.h"
22 #include "sg_cmds_extra.h"
23 #include "sg_unaligned.h"
24 #include "sg_pr2serr.h"
25 
26 /* A utility program originally written for the Linux OS SCSI subsystem.
27  *
28  *
29  * This program issues the SCSI GET LBA STATUS command to the given SCSI
30  * device.
31  */
32 
33 static const char * version_str = "1.07 20151219";
34 
35 #define MAX_GLBAS_BUFF_LEN (1024 * 1024)
36 #define DEF_GLBAS_BUFF_LEN 24
37 
38 static unsigned char glbasBuff[DEF_GLBAS_BUFF_LEN];
39 static unsigned char * glbasBuffp = glbasBuff;
40 
41 
42 static struct option long_options[] = {
43         {"brief", no_argument, 0, 'b'},
44         {"help", no_argument, 0, 'h'},
45         {"hex", no_argument, 0, 'H'},
46         {"lba", required_argument, 0, 'l'},
47         {"maxlen", required_argument, 0, 'm'},
48         {"raw", no_argument, 0, 'r'},
49         {"readonly", no_argument, 0, 'R'},
50         {"verbose", no_argument, 0, 'v'},
51         {"version", no_argument, 0, 'V'},
52         {0, 0, 0, 0},
53 };
54 
55 static void
usage()56 usage()
57 {
58     pr2serr("Usage: sg_get_lba_status  [--brief] [--help] [--hex] "
59             "[--lba=LBA]\n"
60             "                          [--maxlen=LEN] [--raw] [--readonly] "
61             "[--verbose]\n"
62             "                          [--version] DEVICE\n"
63             "  where:\n"
64             "    --brief|-b        a descriptor per line: "
65             "<lba_hex blocks_hex p_status>\n"
66             "                      use twice ('-bb') for given LBA "
67             "provisioning status\n"
68             "    --help|-h         print out usage message\n"
69             "    --hex|-H          output in hexadecimal\n"
70             "    --lba=LBA|-l LBA    starting LBA (logical block address) "
71             "(def: 0)\n"
72             "    --maxlen=LEN|-m LEN    max response length (allocation "
73             "length in cdb)\n"
74             "                           (def: 0 -> %d bytes)\n",
75             DEF_GLBAS_BUFF_LEN );
76     pr2serr("    --raw|-r          output in binary\n"
77             "    --readonly|-R     open DEVICE read-only (def: read-write)\n"
78             "    --verbose|-v      increase verbosity\n"
79             "    --version|-V      print version string and exit\n\n"
80             "Performs a SCSI GET LBA STATUS command (SBC-3)\n"
81             );
82 }
83 
84 static void
dStrRaw(const char * str,int len)85 dStrRaw(const char* str, int len)
86 {
87     int k;
88 
89     for (k = 0 ; k < len; ++k)
90         printf("%c", str[k]);
91 }
92 
93 /* Decodes given LBA status descriptor passing back the starting LBA,
94  * the number of blocks and returns the provisioning status, -1 for error.
95  */
96 static int
decode_lba_status_desc(const unsigned char * ucp,uint64_t * slbap,uint32_t * blocksp)97 decode_lba_status_desc(const unsigned char * ucp, uint64_t * slbap,
98                        uint32_t * blocksp)
99 {
100     uint32_t blocks;
101     uint64_t ull;
102 
103     if (NULL == ucp)
104         return -1;
105     ull = sg_get_unaligned_be64(ucp + 0);
106     blocks = sg_get_unaligned_be32(ucp + 8);
107     if (slbap)
108         *slbap = ull;
109     if (blocksp)
110         *blocksp = blocks;
111     return ucp[12] & 0xf;
112 }
113 
114 
115 int
main(int argc,char * argv[])116 main(int argc, char * argv[])
117 {
118     int sg_fd, k, j, res, c, rlen, num_descs;
119     int do_brief = 0;
120     int do_hex = 0;
121     int64_t ll;
122     uint64_t lba = 0;
123     uint64_t d_lba = 0;
124     uint32_t d_blocks = 0;
125     int maxlen = DEF_GLBAS_BUFF_LEN;
126     int do_raw = 0;
127     int o_readonly = 0;
128     int verbose = 0;
129     const char * device_name = NULL;
130     const unsigned char * ucp;
131     int ret = 0;
132 
133     while (1) {
134         int option_index = 0;
135 
136         c = getopt_long(argc, argv, "bhHl:m:rRvV", long_options,
137                         &option_index);
138         if (c == -1)
139             break;
140 
141         switch (c) {
142         case 'b':
143             ++do_brief;
144             break;
145         case 'h':
146         case '?':
147             usage();
148             return 0;
149         case 'H':
150             ++do_hex;
151             break;
152         case 'l':
153             ll = sg_get_llnum(optarg);
154             if (-1 == ll) {
155                 pr2serr("bad argument to '--lba'\n");
156                 return SG_LIB_SYNTAX_ERROR;
157             }
158             lba = (uint64_t)ll;
159             break;
160         case 'm':
161             maxlen = sg_get_num(optarg);
162             if ((maxlen < 0) || (maxlen > MAX_GLBAS_BUFF_LEN)) {
163                 pr2serr("argument to '--maxlen' should be %d or less\n",
164                         MAX_GLBAS_BUFF_LEN);
165                 return SG_LIB_SYNTAX_ERROR;
166             }
167             break;
168         case 'r':
169             ++do_raw;
170             break;
171         case 'R':
172             ++o_readonly;
173             break;
174         case 'v':
175             ++verbose;
176             break;
177         case 'V':
178             pr2serr("version: %s\n", version_str);
179             return 0;
180         default:
181             pr2serr("unrecognised option code 0x%x ??\n", c);
182             usage();
183             return SG_LIB_SYNTAX_ERROR;
184         }
185     }
186     if (optind < argc) {
187         if (NULL == device_name) {
188             device_name = argv[optind];
189             ++optind;
190         }
191         if (optind < argc) {
192             for (; optind < argc; ++optind)
193                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
194             usage();
195             return SG_LIB_SYNTAX_ERROR;
196         }
197     }
198 
199     if (NULL == device_name) {
200         pr2serr("missing device name!\n");
201         usage();
202         return SG_LIB_SYNTAX_ERROR;
203     }
204     if (maxlen > DEF_GLBAS_BUFF_LEN) {
205         glbasBuffp = (unsigned char *)calloc(maxlen, 1);
206         if (NULL == glbasBuffp) {
207             pr2serr("unable to allocate %d bytes on heap\n", maxlen);
208             return SG_LIB_SYNTAX_ERROR;
209         }
210     }
211     if (do_raw) {
212         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
213             perror("sg_set_binary_mode");
214             ret = SG_LIB_FILE_ERROR;
215             goto free_buff;
216         }
217     }
218 
219     sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
220     if (sg_fd < 0) {
221         pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
222         ret = SG_LIB_FILE_ERROR;
223         goto free_buff;
224     }
225 
226     res = sg_ll_get_lba_status(sg_fd, lba, glbasBuffp, maxlen, 1,
227                                verbose);
228     ret = res;
229     if (0 == res) {
230         /* in sbc3r25 offset for calculating the 'parameter data length'
231          * (rlen variable below) was reduced from 8 to 4. */
232         if (maxlen >= 4)
233             rlen = sg_get_unaligned_be32(glbasBuffp + 0) + 4;
234         else
235             rlen = maxlen;
236         k = (rlen > maxlen) ? maxlen : rlen;
237         if (do_raw) {
238             dStrRaw((const char *)glbasBuffp, k);
239             goto the_end;
240         }
241         if (do_hex) {
242             dStrHex((const char *)glbasBuffp, k, 1);
243             goto the_end;
244         }
245         if (maxlen < 4) {
246             if (verbose)
247                 pr2serr("Exiting because allocation length (maxlen) less "
248                         "than 4\n");
249             goto the_end;
250         }
251         if ((verbose > 1) || (verbose && (rlen > maxlen))) {
252             pr2serr("response length %d bytes\n", rlen);
253             if (rlen > maxlen)
254                 pr2serr("  ... which is greater than maxlen (allocation "
255                         "length %d), truncation\n", maxlen);
256         }
257         if (rlen > maxlen)
258             rlen = maxlen;
259 
260         if (do_brief > 1) {
261             if (rlen < 24) {
262                 pr2serr("Need maxlen and response length to be at least 24, "
263                         "have %d bytes\n", rlen);
264                 ret = SG_LIB_CAT_OTHER;
265                 goto the_end;
266             }
267             res = decode_lba_status_desc(glbasBuffp + 8, &d_lba, &d_blocks);
268             if ((res < 0) || (res > 15)) {
269                 pr2serr("first LBA status descriptor returned %d ??\n", res);
270                 ret = SG_LIB_CAT_OTHER;
271                 goto the_end;
272             }
273             if ((lba < d_lba) || (lba >= (d_lba + d_blocks))) {
274                 pr2serr("given LBA not in range of first descriptor:\n"
275                         "  descriptor LBA: 0x");
276                 for (j = 0; j < 8; ++j)
277                     pr2serr("%02x", glbasBuffp[8 + j]);
278                 pr2serr("  blocks: 0x%x  p_status: %d\n",
279                         (unsigned int)d_blocks, res);
280                 ret = SG_LIB_CAT_OTHER;
281                 goto the_end;
282             }
283             printf("%d\n", res);
284             goto the_end;
285         }
286 
287         if (rlen < 24) {
288             printf("No complete LBA status descriptors available\n");
289             goto the_end;
290         }
291         num_descs = (rlen - 8) / 16;
292         if (verbose)
293             pr2serr("%d complete LBA status descriptors found\n", num_descs);
294         for (ucp = glbasBuffp + 8, k = 0; k < num_descs; ucp += 16, ++k) {
295             res = decode_lba_status_desc(ucp, &d_lba, &d_blocks);
296             if ((res < 0) || (res > 15))
297                 pr2serr("descriptor %d: bad LBA status descriptor returned "
298                         "%d\n", k + 1, res);
299             if (do_brief) {
300                 printf("0x");
301                 for (j = 0; j < 8; ++j)
302                     printf("%02x", ucp[j]);
303                 printf("  0x%x  %d\n", (unsigned int)d_blocks, res);
304             } else {
305                 printf("descriptor LBA: 0x");
306                 for (j = 0; j < 8; ++j)
307                     printf("%02x", ucp[j]);
308                 printf("  blocks: %u", (unsigned int)d_blocks);
309                 switch (res) {
310                 case 0:
311                     printf("  mapped (or unknown)\n");
312                     break;
313                 case 1:
314                     printf("  deallocated\n");
315                     break;
316                 case 2:
317                     printf("  anchored\n");
318                     break;
319                 default:
320                     printf("  Provisioning status: %d\n", res);
321                     break;
322                 }
323             }
324         }
325         if ((num_descs * 16) + 8 < rlen)
326             pr2serr("incomplete trailing LBA status descriptors found\n");
327     } else if (SG_LIB_CAT_INVALID_OP == res)
328         pr2serr("Get LBA Status command not supported\n");
329     else if (SG_LIB_CAT_ILLEGAL_REQ == res)
330         pr2serr("Get LBA Status command: bad field in cdb\n");
331     else {
332         char b[80];
333 
334         sg_get_category_sense_str(res, sizeof(b), b, verbose);
335         pr2serr("Get LBA Status command: %s\n", b);
336     }
337 
338 the_end:
339     res = sg_cmds_close_device(sg_fd);
340     if (res < 0) {
341         pr2serr("close error: %s\n", safe_strerror(-res));
342         if (0 == ret)
343             ret = SG_LIB_FILE_ERROR;
344     }
345 free_buff:
346     if (glbasBuffp && (glbasBuffp != glbasBuff))
347         free(glbasBuffp);
348     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
349 }
350