xref: /aosp_15_r20/external/sg3_utils/src/sg_get_lba_status.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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