1 /*
2 * Copyright (c) 2009-2022 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 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "sg_lib.h"
26 #include "sg_cmds_basic.h"
27 #include "sg_cmds_extra.h"
28 #include "sg_unaligned.h"
29 #include "sg_pr2serr.h"
30
31 /* A utility program originally written for the Linux OS SCSI subsystem.
32 *
33 *
34 * This program issues the SCSI GET LBA STATUS command to the given SCSI
35 * device.
36 */
37
38 static const char * version_str = "1.31 20220807"; /* sbc5r03 */
39
40 #define MY_NAME "sg_get_lba_status"
41
42 #ifndef UINT32_MAX
43 #define UINT32_MAX ((uint32_t)-1)
44 #endif
45
46 #define MAX_GLBAS_BUFF_LEN (1024 * 1024)
47 #define DEF_GLBAS_BUFF_LEN 1024
48 #define MIN_MAXLEN 16
49
50 static uint8_t glbasFixedBuff[DEF_GLBAS_BUFF_LEN];
51
52
53 static struct option long_options[] = {
54 {"16", no_argument, 0, 'S'},
55 {"32", no_argument, 0, 'T'},
56 {"brief", no_argument, 0, 'b'},
57 {"blockhex", no_argument, 0, 'B'},
58 {"element-id", required_argument, 0, 'e'},
59 {"element_id", required_argument, 0, 'e'},
60 {"help", no_argument, 0, 'h'},
61 {"hex", no_argument, 0, 'H'},
62 {"in", required_argument, 0, 'i'}, /* silent, same as --inhex= */
63 {"inhex", required_argument, 0, 'i'},
64 {"json", optional_argument, 0, 'j'},
65 {"lba", required_argument, 0, 'l'},
66 {"maxlen", required_argument, 0, 'm'},
67 {"raw", no_argument, 0, 'r'},
68 {"readonly", no_argument, 0, 'R'},
69 {"report-type", required_argument, 0, 't'},
70 {"report_type", required_argument, 0, 't'},
71 {"scan-len", required_argument, 0, 's'},
72 {"scan_len", required_argument, 0, 's'},
73 {"verbose", no_argument, 0, 'v'},
74 {"version", no_argument, 0, 'V'},
75 {0, 0, 0, 0},
76 };
77
78 static void
usage()79 usage()
80 {
81 pr2serr("Usage: sg_get_lba_status [--16] [--32] [--blockhex] "
82 "[--brief]\n"
83 " [--element-id=EI] [--help] [--hex] "
84 "[--inhex=FN]\n"
85 " [--lba=LBA] [--maxlen=LEN] [--raw] "
86 "[--readonly]\n"
87 " [--report-type=RT] [--scan-len=SL] "
88 "[--verbose]\n"
89 " [--version] DEVICE\n"
90 " where:\n"
91 " --16|-S use GET LBA STATUS(16) cdb (def)\n"
92 " --32|-T use GET LBA STATUS(32) cdb\n"
93 " --blockhex|-B outputs the (number of) blocks field "
94 " in hex\n"
95 " --brief|-b a descriptor per line:\n"
96 " <lba_hex blocks_hex p_status "
97 "add_status>\n"
98 " use twice ('-bb') for given LBA "
99 "provisioning status\n"
100 " --element-id=EI|-e EI EI is the element identifier "
101 "(def: 0)\n"
102 " --help|-h print out usage message\n"
103 " --hex|-H output in hexadecimal\n"
104 " --inhex=FN|-i FN input taken from file FN rather than "
105 "DEVICE,\n"
106 " assumed to be ASCII hex or, if --raw, "
107 "in binary\n"
108 " --json[=JO]|-j[JO] output in JSON instead of human "
109 "readable text.\n"
110 " Use --json=? for JSON help\n"
111 " --lba=LBA|-l LBA starting LBA (logical block address) "
112 "(def: 0)\n"
113 " --maxlen=LEN|-m LEN max response length (allocation "
114 "length in cdb)\n"
115 " (def: 0 -> %d bytes)\n",
116 DEF_GLBAS_BUFF_LEN );
117 pr2serr(" --raw|-r output in binary, unless if --inhex=FN "
118 "is given,\n"
119 " in which case input file is binary\n"
120 " --readonly|-R open DEVICE read-only (def: read-write)\n"
121 " --report-type=RT|-t RT report type: 0->all LBAs (def);\n"
122 " 1-> LBAs with non-zero "
123 "provisioning status\n"
124 " 2-> LBAs that are mapped\n"
125 " 3-> LBAs that are deallocated\n"
126 " 4-> LBAs that are anchored\n"
127 " 16-> LBAs that may return "
128 "unrecovered error\n"
129 " --scan-len=SL|-s SL SL in maximum scan length (unit: "
130 "logical blocks)\n"
131 " (def: 0 which implies no limit)\n"
132 " --verbose|-v increase verbosity\n"
133 " --version|-V print version string and exit\n\n"
134 "Performs a SCSI GET LBA STATUS(16) or GET LBA STATUS(32) "
135 "command (SBC-3 and\nSBC-4). The --element-id=EI and the "
136 "--scan-len=SL fields are only active\non the 32 byte cdb "
137 "variant. If --inhex=FN is given then contents of FN is\n"
138 "assumed to be a response to this command.\n"
139 );
140 }
141
142 static void
dStrRaw(const char * str,int len)143 dStrRaw(const char * str, int len)
144 {
145 int k;
146
147 for (k = 0; k < len; ++k)
148 printf("%c", str[k]);
149 }
150
151 /* Decodes given LBA status descriptor passing back the starting LBA,
152 * the number of blocks and returns the provisioning status, -1 for error.
153 */
154 static int
decode_lba_status_desc(const uint8_t * bp,uint64_t * slbap,uint32_t * blocksp,uint8_t * add_statusp)155 decode_lba_status_desc(const uint8_t * bp, uint64_t * slbap,
156 uint32_t * blocksp, uint8_t * add_statusp)
157 {
158 uint32_t blocks;
159 uint64_t ull;
160
161 if (NULL == bp)
162 return -1;
163 ull = sg_get_unaligned_be64(bp + 0);
164 blocks = sg_get_unaligned_be32(bp + 8);
165 if (slbap)
166 *slbap = ull;
167 if (blocksp)
168 *blocksp = blocks;
169 if (add_statusp)
170 *add_statusp = bp[13];
171 return bp[12] & 0xf;
172 }
173
174 static char *
get_prov_status_str(int ps,char * b,int blen)175 get_prov_status_str(int ps, char * b, int blen)
176 {
177 switch (ps) {
178 case 0:
179 sg_scnpr(b, blen, "mapped (or unknown)");
180 break;
181 case 1:
182 sg_scnpr(b, blen, "deallocated");
183 break;
184 case 2:
185 sg_scnpr(b, blen, "anchored");
186 break;
187 case 3:
188 sg_scnpr(b, blen, "mapped"); /* sbc4r12 */
189 break;
190 case 4:
191 sg_scnpr(b, blen, "unknown"); /* sbc4r12 */
192 break;
193 default:
194 sg_scnpr(b, blen, "unknown provisioning status: %d", ps);
195 break;
196 }
197 return b;
198 }
199
200 static char *
get_pr_status_str(int as,char * b,int blen)201 get_pr_status_str(int as, char * b, int blen)
202 {
203 switch (as) {
204 case 0:
205 sg_scnpr(b, blen, "%s", "");
206 break;
207 case 1:
208 sg_scnpr(b, blen, "may contain unrecovered errors");
209 break;
210 default:
211 sg_scnpr(b, blen, "unknown additional status: %d", as);
212 break;
213 }
214 return b;
215 }
216
217
218 int
main(int argc,char * argv[])219 main(int argc, char * argv[])
220 {
221 bool do_16 = false;
222 bool do_32 = false;
223 bool do_raw = false;
224 bool no_final_msg = false;
225 bool o_readonly = false;
226 bool verbose_given = false;
227 bool version_given = false;
228 int k, j, res, c, n, rlen, num_descs, completion_cond, in_len;
229 int sg_fd = -1;
230 int blockhex = 0;
231 int do_brief = 0;
232 int do_hex = 0;
233 int ret = 0;
234 int maxlen = DEF_GLBAS_BUFF_LEN;
235 int rt = 0;
236 int verbose = 0;
237 uint8_t add_status = 0; /* keep gcc quiet */
238 uint64_t d_lba = 0;
239 uint32_t d_blocks = 0;
240 uint32_t element_id = 0;
241 uint32_t scan_len = 0;
242 int64_t ll;
243 uint64_t lba = 0;
244 const char * device_name = NULL;
245 const char * in_fn = NULL;
246 const uint8_t * bp;
247 uint8_t * glbasBuffp = glbasFixedBuff;
248 uint8_t * free_glbasBuffp = NULL;
249 sgj_opaque_p jop = NULL;
250 sgj_opaque_p jo2p = NULL;
251 sgj_opaque_p jap = NULL;
252 sgj_state json_st SG_C_CPP_ZERO_INIT;
253 sgj_state * jsp = &json_st;
254 char b[144];
255 static const size_t blen = sizeof(b);
256 static const char * prov_stat_s = "Provisoning status";
257 static const char * add_stat_s = "Additional status";
258 static const char * compl_cond_s = "Completion condition";
259
260 while (1) {
261 int option_index = 0;
262
263 c = getopt_long(argc, argv, "bBe:hi:j::Hl:m:rRs:St:TvV",
264 long_options, &option_index);
265 if (c == -1)
266 break;
267
268 switch (c) {
269 case 'b':
270 ++do_brief;
271 break;
272 case 'B':
273 ++blockhex;
274 break;
275 case 'e':
276 ll = sg_get_llnum(optarg);
277 if ((ll < 0) || (ll > UINT32_MAX)) {
278 pr2serr("bad argument to '--element-id'\n");
279 return SG_LIB_SYNTAX_ERROR;
280 }
281 element_id = (uint32_t)ll;
282 break;
283 case 'h':
284 case '?':
285 usage();
286 return 0;
287 case 'H':
288 ++do_hex;
289 break;
290 case 'i':
291 in_fn = optarg;
292 break;
293 case 'j':
294 if (! sgj_init_state(&json_st, optarg)) {
295 int bad_char = json_st.first_bad_char;
296 char e[1500];
297
298 if (bad_char) {
299 pr2serr("bad argument to --json= option, unrecognized "
300 "character '%c'\n\n", bad_char);
301 }
302 sg_json_usage(0, e, sizeof(e));
303 pr2serr("%s", e);
304 return SG_LIB_SYNTAX_ERROR;
305 }
306 break;
307 case 'l':
308 ll = sg_get_llnum(optarg);
309 if (-1 == ll) {
310 pr2serr("bad argument to '--lba'\n");
311 return SG_LIB_SYNTAX_ERROR;
312 }
313 lba = (uint64_t)ll;
314 break;
315 case 'm':
316 maxlen = sg_get_num(optarg);
317 if ((maxlen < 0) || (maxlen > MAX_GLBAS_BUFF_LEN)) {
318 pr2serr("argument to '--maxlen' should be %d or less\n",
319 MAX_GLBAS_BUFF_LEN);
320 return SG_LIB_SYNTAX_ERROR;
321 }
322 if (0 == maxlen)
323 maxlen = DEF_GLBAS_BUFF_LEN;
324 else if (maxlen < MIN_MAXLEN) {
325 pr2serr("Warning: --maxlen=LEN less than %d ignored\n",
326 MIN_MAXLEN);
327 maxlen = DEF_GLBAS_BUFF_LEN;
328 }
329 break;
330 case 'r':
331 do_raw = true;
332 break;
333 case 'R':
334 o_readonly = true;
335 break;
336 case 's':
337 ll = sg_get_llnum(optarg);
338 if ((ll < 0) || (ll > UINT32_MAX)) {
339 pr2serr("bad argument to '--scan-len'\n");
340 return SG_LIB_SYNTAX_ERROR;
341 }
342 scan_len = (uint32_t)ll;
343 break;
344 case 'S':
345 do_16 = true;
346 break;
347 case 't':
348 rt = sg_get_num_nomult(optarg);
349 if ((rt < 0) || (rt > 255)) {
350 pr2serr("'--report-type=RT' should be between 0 and 255 "
351 "(inclusive)\n");
352 return SG_LIB_SYNTAX_ERROR;
353 }
354 break;
355 case 'T':
356 do_32 = true;
357 break;
358 case 'v':
359 verbose_given = true;
360 ++verbose;
361 break;
362 case 'V':
363 version_given = true;
364 break;
365 default:
366 pr2serr("unrecognised option code 0x%x ??\n", c);
367 usage();
368 return SG_LIB_SYNTAX_ERROR;
369 }
370 }
371 if (optind < argc) {
372 if (NULL == device_name) {
373 device_name = argv[optind];
374 ++optind;
375 }
376 if (optind < argc) {
377 for (; optind < argc; ++optind)
378 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
379 usage();
380 return SG_LIB_SYNTAX_ERROR;
381 }
382 }
383
384 #ifdef DEBUG
385 pr2serr("In DEBUG mode, ");
386 if (verbose_given && version_given) {
387 pr2serr("but override: '-vV' given, zero verbose and continue\n");
388 verbose_given = false;
389 version_given = false;
390 verbose = 0;
391 } else if (! verbose_given) {
392 pr2serr("set '-vv'\n");
393 verbose = 2;
394 } else
395 pr2serr("keep verbose=%d\n", verbose);
396 #else
397 if (verbose_given && version_given)
398 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
399 #endif
400 if (version_given) {
401 pr2serr("version: %s\n", version_str);
402 return 0;
403 }
404 if (jsp->pr_as_json)
405 jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
406
407 if (maxlen > DEF_GLBAS_BUFF_LEN) {
408 glbasBuffp = (uint8_t *)sg_memalign(maxlen, 0, &free_glbasBuffp,
409 verbose > 3);
410 if (NULL == glbasBuffp) {
411 pr2serr("unable to allocate %d bytes on heap\n", maxlen);
412 return sg_convert_errno(ENOMEM);
413 }
414 }
415 if (device_name && in_fn) {
416 pr2serr("ignoring DEVICE, best to give DEVICE or --inhex=FN, but "
417 "not both\n");
418 device_name = NULL;
419 }
420 if (NULL == device_name) {
421 if (in_fn) {
422 if ((ret = sg_f2hex_arr(in_fn, do_raw, false, glbasBuffp,
423 &in_len, maxlen))) {
424 if (SG_LIB_LBA_OUT_OF_RANGE == ret) {
425 no_final_msg = true;
426 pr2serr("... decode what we have, --maxlen=%d needs to "
427 "be increased\n", maxlen);
428 } else
429 goto fini;
430 }
431 if (verbose > 2)
432 pr2serr("Read %d [0x%x] bytes of user supplied data\n",
433 in_len, in_len);
434 if (do_raw)
435 do_raw = false; /* can interfere on decode */
436 if (in_len < 4) {
437 pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
438 in_fn, in_len);
439 ret = SG_LIB_SYNTAX_ERROR;
440 goto fini;
441 }
442 goto start_response;
443 } else {
444 pr2serr("missing device name!\n\n");
445 usage();
446 ret = SG_LIB_FILE_ERROR;
447 no_final_msg = true;
448 goto fini;
449 }
450 }
451 if (do_raw) {
452 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
453 perror("sg_set_binary_mode");
454 ret = SG_LIB_FILE_ERROR;
455 goto fini;
456 }
457 }
458 if (do_16 && do_32) {
459 pr2serr("both --16 and --32 given, choose --16\n");
460 do_32 = false;
461 } else if ((! do_16) && (! do_32)) {
462 if (verbose > 3)
463 pr2serr("choosing --16\n");
464 do_16 = true;
465 }
466 if (do_16) {
467 if (element_id != 0)
468 pr2serr("Warning: --element_id= ignored with 16 byte cdb\n");
469 if (scan_len != 0)
470 pr2serr("Warning: --scan_len= ignored with 16 byte cdb\n");
471 }
472 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
473 if (sg_fd < 0) {
474 pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
475 ret = sg_convert_errno(-sg_fd);
476 goto fini;
477 }
478
479 res = 0;
480 if (do_16)
481 res = sg_ll_get_lba_status16(sg_fd, lba, rt, glbasBuffp, maxlen, true,
482 verbose);
483 else if (do_32) /* keep analyser happy since do_32 must be true */
484 res = sg_ll_get_lba_status32(sg_fd, lba, scan_len, element_id, rt,
485 glbasBuffp, maxlen, true, verbose);
486
487 ret = res;
488 if (res)
489 goto error;
490
491 start_response:
492 /* in sbc3r25 offset for calculating the 'parameter data length'
493 * (rlen variable below) was reduced from 8 to 4. */
494 if (maxlen >= 4)
495 rlen = sg_get_unaligned_be32(glbasBuffp + 0) + 4;
496 else
497 rlen = maxlen;
498 k = (rlen > maxlen) ? maxlen : rlen;
499 if (do_raw) {
500 dStrRaw((const char *)glbasBuffp, k);
501 goto fini;
502 }
503 if (do_hex) {
504 if (do_hex > 2)
505 hex2stdout(glbasBuffp, k, -1);
506 else
507 hex2stdout(glbasBuffp, k, (2 == do_hex) ? 0 : 1);
508 goto fini;
509 }
510 if (maxlen < 4) {
511 if (verbose)
512 pr2serr("Exiting because allocation length (maxlen) less "
513 "than 4\n");
514 goto fini;
515 }
516 if ((verbose > 1) || (verbose && (rlen > maxlen))) {
517 pr2serr("response length %d bytes\n", rlen);
518 if (rlen > maxlen)
519 pr2serr(" ... which is greater than maxlen (allocation "
520 "length %d), truncation\n", maxlen);
521 }
522 if (rlen > maxlen)
523 rlen = maxlen;
524
525 if (do_brief > 1) {
526 if (rlen > DEF_GLBAS_BUFF_LEN) {
527 pr2serr("Need maxlen and response length to be at least %d, "
528 "have %d bytes\n", DEF_GLBAS_BUFF_LEN, rlen);
529 ret = SG_LIB_CAT_OTHER;
530 goto fini;
531 }
532 res = decode_lba_status_desc(glbasBuffp + 8, &d_lba, &d_blocks,
533 &add_status);
534 if ((res < 0) || (res > 15)) {
535 pr2serr("first LBA status descriptor returned %d ??\n", res);
536 ret = SG_LIB_LOGIC_ERROR;
537 goto fini;
538 }
539 if ((lba < d_lba) || (lba >= (d_lba + d_blocks))) {
540 pr2serr("given LBA not in range of first descriptor:\n"
541 " descriptor LBA: 0x");
542 for (j = 0; j < 8; ++j)
543 pr2serr("%02x", glbasBuffp[8 + j]);
544 pr2serr(" blocks: 0x%x p_status: %d add_status: 0x%x\n",
545 (unsigned int)d_blocks, res,
546 (unsigned int)add_status);
547 ret = SG_LIB_CAT_OTHER;
548 goto fini;
549 }
550 sgj_pr_hr(jsp,"p_status: %d add_status: 0x%x\n", res,
551 (unsigned int)add_status);
552 if (jsp->pr_as_json) {
553 sgj_js_nv_i(jsp, jop, prov_stat_s, res);
554 sgj_js_nv_i(jsp, jop, add_stat_s, add_status);
555 }
556 goto fini;
557 }
558
559 if (rlen < 24) {
560 sgj_pr_hr(jsp, "No complete LBA status descriptors available\n");
561 goto fini;
562 }
563 num_descs = (rlen - 8) / 16;
564 completion_cond = (*(glbasBuffp + 7) >> 1) & 7; /* added sbc4r14 */
565 if (do_brief)
566 sgj_haj_vi(jsp, jop, 0, compl_cond_s, SGJ_SEP_EQUAL_NO_SPACE,
567 completion_cond, true);
568 else {
569 switch (completion_cond) {
570 case 0:
571 snprintf(b, blen, "No indication of the completion condition");
572 break;
573 case 1:
574 snprintf(b, blen, "Command completed due to meeting allocation "
575 "length");
576 break;
577 case 2:
578 snprintf(b, blen, "Command completed due to meeting scan length");
579 break;
580 case 3:
581 snprintf(b, blen, "Command completed due to meeting capacity of "
582 "medium");
583 break;
584 default:
585 snprintf(b, blen, "Command completion is reserved [%d]",
586 completion_cond);
587 break;
588 }
589 sgj_pr_hr(jsp, "%s\n", b);
590 sgj_js_nv_istr(jsp, jop, compl_cond_s, completion_cond,
591 NULL /* "meaning" */, b);
592 }
593 sgj_haj_vi(jsp, jop, 0, "RTP", SGJ_SEP_EQUAL_NO_SPACE,
594 *(glbasBuffp + 7) & 0x1, true); /* added sbc4r12 */
595 if (verbose)
596 pr2serr("%d complete LBA status descriptors found\n", num_descs);
597 if (jsp->pr_as_json)
598 jap = sgj_named_subarray_r(jsp, jop, "lba_status_descriptor");
599
600 for (bp = glbasBuffp + 8, k = 0; k < num_descs; bp += 16, ++k) {
601 res = decode_lba_status_desc(bp, &d_lba, &d_blocks, &add_status);
602 if ((res < 0) || (res > 15))
603 pr2serr("descriptor %d: bad LBA status descriptor returned "
604 "%d\n", k + 1, res);
605 if (jsp->pr_as_json)
606 jo2p = sgj_new_unattached_object_r(jsp);
607 if (do_brief) {
608 n = 0;
609 n += sg_scnpr(b + n, blen - n, "0x");
610 for (j = 0; j < 8; ++j)
611 n += sg_scnpr(b + n, blen - n, "%02x", bp[j]);
612 if ((0 == blockhex) || (1 == (blockhex % 2)))
613 n += sg_scnpr(b + n, blen - n, " 0x%x %d %d",
614 (unsigned int)d_blocks, res, add_status);
615 else
616 n += sg_scnpr(b + n, blen - n, " %u %d %d",
617 (unsigned int)d_blocks, res, add_status);
618 sgj_pr_hr(jsp, "%s\n", b);
619 sgj_js_nv_ihex(jsp, jo2p, "lba", d_lba);
620 sgj_js_nv_ihex(jsp, jo2p, "blocks", d_blocks);
621 sgj_js_nv_i(jsp, jo2p, prov_stat_s, res);
622 sgj_js_nv_i(jsp, jo2p, add_stat_s, add_status);
623 } else {
624 if (jsp->pr_as_json) {
625 sgj_js_nv_ihex(jsp, jo2p, "lba", d_lba);
626 sgj_js_nv_ihex(jsp, jo2p, "blocks", d_blocks);
627 sgj_js_nv_istr(jsp, jo2p, prov_stat_s, res, NULL,
628 get_prov_status_str(res, b, blen));
629 sgj_js_nv_istr(jsp, jo2p, add_stat_s, add_status, NULL,
630 get_pr_status_str(add_status, b, blen));
631 } else {
632 char d[64];
633
634 n = 0;
635 n += sg_scnpr(b + n, blen - n, "[%d] LBA: 0x", k + 1);
636 for (j = 0; j < 8; ++j)
637 n += sg_scnpr(b + n, blen - n, "%02x", bp[j]);
638 if (1 == (blockhex % 2)) {
639
640 snprintf(d, sizeof(d), "0x%x", d_blocks);
641 n += sg_scnpr(b + n, blen - n, " blocks: %10s", d);
642 } else
643 n += sg_scnpr(b + n, blen - n, " blocks: %10u",
644 (unsigned int)d_blocks);
645 get_prov_status_str(res, d, sizeof(d));
646 n += sg_scnpr(b + n, blen - n, " %s", d);
647 get_pr_status_str(add_status, d, sizeof(d));
648 if (strlen(d) > 0)
649 n += sg_scnpr(b + n, blen - n, " [%s]", d);
650 sgj_pr_hr(jsp, "%s\n", b);
651 }
652 }
653 if (jsp->pr_as_json)
654 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
655 }
656 if ((num_descs * 16) + 8 < rlen)
657 pr2serr("incomplete trailing LBA status descriptors found\n");
658 goto fini;
659
660 error:
661 if (SG_LIB_CAT_INVALID_OP == res)
662 pr2serr("Get LBA Status command not supported\n");
663 else if (SG_LIB_CAT_ILLEGAL_REQ == res)
664 pr2serr("Get LBA Status command: bad field in cdb\n");
665 else {
666 sg_get_category_sense_str(res, sizeof(b), b, verbose);
667 pr2serr("Get LBA Status command: %s\n", b);
668 }
669
670 fini:
671 if (sg_fd >= 0) {
672 res = sg_cmds_close_device(sg_fd);
673 if (res < 0) {
674 pr2serr("close error: %s\n", safe_strerror(-res));
675 if (0 == ret)
676 ret = sg_convert_errno(-res);
677 }
678 }
679 if (free_glbasBuffp)
680 free(free_glbasBuffp);
681 if ((0 == verbose) && (! no_final_msg)) {
682 if (! sg_if_can2stderr("sg_get_lba_status failed: ", ret))
683 pr2serr("Some error occurred, try again with '-v' or '-vv' for "
684 "more information\n");
685 }
686 ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
687 if (jsp->pr_as_json) {
688 if (0 == do_hex)
689 sgj_js2file(jsp, NULL, ret, stdout);
690 sgj_finish(jsp);
691 }
692 return ret;
693 }
694