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