xref: /aosp_15_r20/external/sg3_utils/src/sg_luns.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2004-2021 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 <stdbool.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <ctype.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_unaligned.h"
28 #include "sg_pr2serr.h"
29 
30 /* A utility program originally written for the Linux OS SCSI subsystem.
31  *
32  *
33  * This program issues the SCSI REPORT LUNS command to the given SCSI device
34  * and decodes the response.
35  */
36 
37 static const char * version_str = "1.48 20210804";      /* spc6r05 */
38 
39 #define MAX_RLUNS_BUFF_LEN (1024 * 1024)
40 #define DEF_RLUNS_BUFF_LEN (1024 * 8)
41 
42 
43 static struct option long_options[] = {
44         {"decode", no_argument, 0, 'd'},
45         {"help", no_argument, 0, 'h'},
46         {"hex", no_argument, 0, 'H'},
47 #ifdef SG_LIB_LINUX
48         {"linux", no_argument, 0, 'l'},
49 #endif
50         {"lu_cong", no_argument, 0, 'L'},
51         {"lu-cong", no_argument, 0, 'L'},
52         {"maxlen", required_argument, 0, 'm'},
53         {"quiet", no_argument, 0, 'q'},
54         {"raw", no_argument, 0, 'r'},
55         {"readonly", no_argument, 0, 'R'},
56         {"select", required_argument, 0, 's'},
57         {"test", required_argument, 0, 't'},
58         {"verbose", no_argument, 0, 'v'},
59         {"version", no_argument, 0, 'V'},
60         {0, 0, 0, 0},
61 };
62 
63 
64 static void
usage()65 usage()
66 {
67 #ifdef SG_LIB_LINUX
68     pr2serr("Usage: sg_luns    [--decode] [--help] [--hex] [--linux] "
69             "[--lu_cong]\n"
70             "                  [--maxlen=LEN] [--quiet] [--raw] "
71             "[--readonly]\n"
72             "                  [--select=SR] [--verbose] [--version] "
73             "DEVICE\n");
74 #else
75     pr2serr("Usage: sg_luns    [--decode] [--help] [--hex] [--lu_cong] "
76             "[--maxlen=LEN]\n"
77             "                  [--quiet] [--raw] [--readonly] "
78             "[--select=SR]\n"
79             "                  [--verbose] [--version] DEVICE\n");
80 #endif
81     pr2serr("     or\n"
82             "       sg_luns    --test=ALUN [--decode] [--hex] [--lu_cong] "
83             "[--verbose]\n"
84             "  where:\n"
85             "    --decode|-d        decode all luns into component parts\n"
86             "    --help|-h          print out usage message\n"
87             "    --hex|-H           output response in hexadecimal; used "
88             "twice\n"
89             "                       shows decoded values in hex\n");
90 #ifdef SG_LIB_LINUX
91     pr2serr("    --linux|-l         show Linux integer lun after T10 "
92             "representation\n");
93 #endif
94     pr2serr("    --lu_cong|-L       decode as if LU_CONG is set; used "
95             "twice:\n"
96             "                       decode as if LU_CONG is clear\n"
97             "    --maxlen=LEN|-m LEN    max response length (allocation "
98             "length in cdb)\n"
99             "                           (def: 0 -> %d bytes)\n"
100             "    --quiet|-q         output only ASCII hex lun values\n"
101             "    --raw|-r           output response in binary\n"
102             "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
103             "    --select=SR|-s SR    select report SR (def: 0)\n"
104             "                          0 -> luns apart from 'well "
105             "known' lus\n"
106             "                          1 -> only 'well known' "
107             "logical unit numbers\n"
108             "                          2 -> all luns\n"
109             "                          0x10 -> administrative luns\n"
110             "                          0x11 -> admin luns + "
111             "non-conglomerate luns\n"
112             "                          0x12 -> admin lun + its "
113             "subsidiary luns\n"
114             "    --test=ALUN|-t ALUN    decode ALUN and ignore most other "
115             "options\n"
116             "                           and DEVICE (apart from '-H')\n"
117             "    --verbose|-v       increase verbosity\n"
118             "    --version|-V       print version string and exit\n\n"
119             "Performs a SCSI REPORT LUNS command or decodes the given ALUN. "
120             "When SR is\n0x10 or 0x11 DEVICE must be LUN 0 or REPORT LUNS "
121             "well known logical unit;\nwhen SR is 0x12 DEVICE must be an "
122             "administrative logical unit. When the\n--test=ALUN option is "
123             "given, decodes ALUN rather than sending a REPORT\nLUNS "
124             "command.\n", DEF_RLUNS_BUFF_LEN );
125 }
126 
127 /* Decoded according to SAM-5 rev 10. Note that one draft: BCC rev 0,
128  * defines its own "bridge addressing method" in place of the SAM-3
129  * "logical addressing method".  */
130 static void
decode_lun(const char * leadin,const uint8_t * lunp,bool lu_cong,int do_hex,int verbose)131 decode_lun(const char * leadin, const uint8_t * lunp, bool lu_cong,
132            int do_hex, int verbose)
133 {
134     bool next_level, admin_lu_cong;
135     int k, x, a_method, bus_id, target, lun, len_fld, e_a_method;
136     uint64_t ull;
137     char l_leadin[128];
138     char b[256];
139 
140     if (0xff == lunp[0]) {
141         printf("%sLogical unit _not_ specified\n", leadin);
142         return;
143     }
144     admin_lu_cong = lu_cong;
145     memset(l_leadin, 0, sizeof(l_leadin));
146     for (k = 0; k < 4; ++k, lunp += 2) {
147         next_level = false;
148         strncpy(l_leadin, leadin, sizeof(l_leadin) - 3);
149         if (k > 0) {
150             if (lu_cong) {
151                 admin_lu_cong = false;
152                 if ((0 == lunp[0]) && (0 == lunp[1])) {
153                     printf("%s>>>> Administrative LU\n", l_leadin);
154                     if (do_hex || verbose)
155                          printf("        since Subsidiary element is "
156                                 "0x0000\n");
157                     break;
158                 } else
159                     printf("%s>>Subsidiary element:\n", l_leadin);
160             } else
161                 printf("%s>>%s level addressing:\n", l_leadin, ((1 == k) ?
162                          "Second" : ((2 == k) ? "Third" : "Fourth")));
163             strcat(l_leadin, "  ");
164         } else if (lu_cong) {
165             printf("%s>>Administrative element:\n", l_leadin);
166             strcat(l_leadin, "  ");
167         }
168         a_method = (lunp[0] >> 6) & 0x3;
169         switch (a_method) {
170         case 0:         /* peripheral device addressing method */
171             if (lu_cong) {
172                 snprintf(b, sizeof(b), "%sSimple lu addressing: ",
173                          l_leadin);
174                 x = 0x3fff & sg_get_unaligned_be16(lunp + 0);
175                 if (do_hex)
176                     printf("%s0x%04x\n", b, x);
177                 else
178                     printf("%s%d\n", b, x);
179                 if (admin_lu_cong)
180                     next_level = true;
181             } else {
182                 bus_id = lunp[0] & 0x3f;
183                 snprintf(b, sizeof(b), "%sPeripheral device addressing: ",
184                          l_leadin);
185                 if ((0 == bus_id) && (0 == verbose)) {
186                     if (do_hex)
187                         printf("%slun=0x%02x\n", b, lunp[1]);
188                     else
189                         printf("%slun=%d\n", b, lunp[1]);
190                 } else {
191                     if (do_hex)
192                         printf("%sbus_id=0x%02x, %s=0x%02x\n", b, bus_id,
193                                (bus_id ? "target" : "lun"), lunp[1]);
194                     else
195                         printf("%sbus_id=%d, %s=%d\n", b, bus_id,
196                                (bus_id ? "target" : "lun"), lunp[1]);
197                 }
198                 if (bus_id)
199                     next_level = true;
200             }
201             break;
202         case 1:         /* flat space addressing method */
203             lun = 0x3fff & sg_get_unaligned_be16(lunp + 0);
204             if (lu_cong) {
205                 printf("%sSince LU_CONG=1, unexpected Flat space "
206                        "addressing: lun=0x%04x\n", l_leadin, lun);
207                 break;
208             }
209             if (do_hex)
210                 printf("%sFlat space addressing: lun=0x%04x\n", l_leadin,
211                        lun);
212             else
213                 printf("%sFlat space addressing: lun=%d\n", l_leadin, lun);
214             break;
215         case 2:         /* logical unit addressing method */
216             target = (lunp[0] & 0x3f);
217             bus_id = (lunp[1] >> 5) & 0x7;
218             lun = lunp[1] & 0x1f;
219             if (lu_cong) {
220                 printf("%sSince LU_CONG=1, unexpected lu addressing: "
221                        "bus_id=0x%x, target=0x%02x, lun=0x%02x\n", l_leadin,
222                        bus_id, target, lun);
223                 break;
224             }
225             if (do_hex)
226                 printf("%sLogical unit addressing: bus_id=0x%x, "
227                        "target=0x%02x, lun=0x%02x\n", l_leadin, bus_id,
228                        target, lun);
229             else
230                 printf("%sLogical unit addressing: bus_id=%d, target=%d, "
231                        "lun=%d\n", l_leadin, bus_id, target, lun);
232             break;
233         case 3:         /* extended logical unit + flat space addressing */
234             len_fld = (lunp[0] & 0x30) >> 4;
235             e_a_method = lunp[0] & 0xf;
236             x = lunp[1];
237             if ((0 == len_fld) && (1 == e_a_method)) {
238                 snprintf(b, sizeof(b), "well known logical unit");
239                 switch (x) {
240                 case 1:
241                     printf("%sREPORT LUNS %s\n", l_leadin, b);
242                     break;
243                 case 2:         /* obsolete in spc5r01 */
244                     printf("%sACCESS CONTROLS %s\n", l_leadin, b);
245                     break;
246                 case 3:
247                     printf("%sTARGET LOG PAGES %s\n", l_leadin, b);
248                     break;
249                 case 4:
250                     printf("%sSECURITY PROTOCOL %s\n", l_leadin, b);
251                     break;
252                 case 5:
253                     printf("%sMANAGEMENT PROTOCOL %s\n", l_leadin, b);
254                     break;
255                 case 6:
256                     printf("%sTARGET COMMANDS %s\n", l_leadin, b);
257                     break;
258                 default:
259                     if (do_hex)
260                         printf("%s%s 0x%02x\n", l_leadin, b, x);
261                     else
262                         printf("%s%s %d\n", l_leadin, b, x);
263                     break;
264                 }
265             } else if ((1 == len_fld) && (2 == e_a_method)) {
266                 x = sg_get_unaligned_be24(lunp + 1);
267                 if (do_hex)
268                     printf("%sExtended flat space addressing: lun=0x%06x\n",
269                            l_leadin, x);
270                 else
271                     printf("%sExtended flat space addressing: lun=%d\n",
272                            l_leadin, x);
273             } else if ((2 == len_fld) && (2 == e_a_method)) {
274                 ull = sg_get_unaligned_be(5, lunp + 1);
275                 if (do_hex)
276                     printf("%sLong extended flat space addressing: "
277                            "lun=0x%010" PRIx64 "\n", l_leadin, ull);
278                 else
279                     printf("%sLong extended flat space addressing: "
280                            "lun=%" PRIu64 "\n", l_leadin, ull);
281             } else if ((3 == len_fld) && (0xf == e_a_method))
282                 printf("%sLogical unit _not_ specified addressing\n",
283                        l_leadin);
284             else {
285                 if (len_fld < 2) {
286                     if (1 == len_fld)
287                         x = sg_get_unaligned_be24(lunp + 1);
288                     if (do_hex)
289                         printf("%sExtended logical unit addressing: "
290                                "length=%d, e.a. method=%d, value=0x%06x\n",
291                                l_leadin, len_fld, e_a_method, x);
292                     else
293                         printf("%sExtended logical unit addressing: "
294                                "length=%d, e.a. method=%d, value=%d\n",
295                                l_leadin, len_fld, e_a_method, x);
296                 } else {
297                     ull = sg_get_unaligned_be(((2 == len_fld) ? 5 : 7),
298                                               lunp + 1);
299                     if (do_hex) {
300                         printf("%sExtended logical unit addressing: "
301                                "length=%d, e. a. method=%d, ", l_leadin,
302                                len_fld, e_a_method);
303                         if (5 == len_fld)
304                                 printf("value=0x%010" PRIx64 "\n", ull);
305                         else
306                                 printf("value=0x%014" PRIx64 "\n", ull);
307                     } else
308                         printf("%sExtended logical unit addressing: "
309                                "length=%d, e. a. method=%d, value=%" PRIu64
310                                "\n", l_leadin, len_fld, e_a_method, ull);
311                 }
312             }
313             break;
314         }
315         if (next_level)
316             continue;
317         if ((2 == a_method) && (k < 3) && (lunp[2] || lunp[3]))
318             printf("%s<<unexpected data at next level, continue>>\n",
319                    l_leadin);
320         break;
321     }
322 }
323 
324 #ifdef SG_LIB_LINUX
325 static void
linux2t10_lun(uint64_t linux_lun,uint8_t t10_lun[])326 linux2t10_lun(uint64_t linux_lun, uint8_t t10_lun[])
327 {
328     int k;
329 
330     for (k = 0; k < 8; k += 2, linux_lun >>= 16)
331         sg_put_unaligned_be16((uint16_t)linux_lun, t10_lun + k);
332 }
333 
334 static uint64_t
t10_2linux_lun(const uint8_t t10_lun[])335 t10_2linux_lun(const uint8_t t10_lun[])
336 {
337     int k;
338     const uint8_t * cp;
339     uint64_t res;
340 
341     res = sg_get_unaligned_be16(t10_lun + 6);
342     for (cp = t10_lun + 4, k = 0; k < 3; ++k, cp -= 2)
343         res = (res << 16) + sg_get_unaligned_be16(cp);
344     return res;
345 }
346 #endif  /* SG_LIB_LINUX */
347 
348 
349 static void
dStrRaw(const char * str,int len)350 dStrRaw(const char * str, int len)
351 {
352     int k;
353 
354     for (k = 0; k < len; ++k)
355         printf("%c", str[k]);
356 }
357 
358 int
main(int argc,char * argv[])359 main(int argc, char * argv[])
360 {
361 #ifdef SG_LIB_LINUX
362     bool do_linux = false;
363 #endif
364     bool do_quiet = false;
365     bool do_raw = false;
366     bool lu_cong_arg_given = false;
367     bool o_readonly = false;
368 #ifdef SG_LIB_LINUX
369     bool test_linux_in = false;
370     bool test_linux_out = false;
371 #endif
372     bool trunc;
373     bool verbose_given = false;
374     bool version_given = false;
375     int sg_fd, k, m, off, res, c, list_len, len_cap, luns;
376     int decode_arg = 0;
377     int do_hex = 0;
378     int lu_cong_arg = 0;
379     int maxlen = 0;
380     int ret = 0;
381     int select_rep = 0;
382     int verbose = 0;
383     unsigned int h;
384     const char * test_arg = NULL;
385     const char * device_name = NULL;
386     const char * cp;
387     uint8_t * reportLunsBuff = NULL;
388     uint8_t * free_reportLunsBuff = NULL;
389     uint8_t lun_arr[8];
390     struct sg_simple_inquiry_resp sir;
391 
392     while (1) {
393         int option_index = 0;
394 
395 #ifdef SG_LIB_LINUX
396         c = getopt_long(argc, argv, "dhHlLm:qrRs:t:vV", long_options,
397                         &option_index);
398 #else
399         c = getopt_long(argc, argv, "dhHLm:qrRs:t:vV", long_options,
400                         &option_index);
401 #endif
402         if (c == -1)
403             break;
404 
405         switch (c) {
406         case 'd':
407             ++decode_arg;
408             break;
409         case 'h':
410         case '?':
411             usage();
412             return 0;
413         case 'H':
414             ++do_hex;
415             break;
416 #ifdef SG_LIB_LINUX
417         case 'l':
418             do_linux = false;
419             break;
420 #endif
421         case 'L':
422             ++lu_cong_arg;
423             lu_cong_arg_given = true;
424             break;
425         case 'm':
426             maxlen = sg_get_num(optarg);
427             if ((maxlen < 0) || (maxlen > MAX_RLUNS_BUFF_LEN)) {
428                 pr2serr("argument to '--maxlen' should be %d or less\n",
429                         MAX_RLUNS_BUFF_LEN);
430                 return SG_LIB_SYNTAX_ERROR;
431             } else if (maxlen < 4) {
432                 pr2serr("Warning: setting '--maxlen' to 4\n");
433                 maxlen = 4;
434             }
435             break;
436         case 'q':
437             do_quiet = true;
438             break;
439         case 'r':
440             do_raw = true;
441             break;
442         case 'R':
443             o_readonly = true;
444             break;
445         case 's':
446            select_rep = sg_get_num(optarg);
447            if ((select_rep < 0) || (select_rep > 255)) {
448                 pr2serr("bad argument to '--select', expect 0 to 255\n");
449                 return SG_LIB_SYNTAX_ERROR;
450             }
451             break;
452         case 't':
453             test_arg = optarg;
454             break;
455         case 'v':
456             verbose_given = true;
457             ++verbose;
458             break;
459         case 'V':
460             version_given = true;
461             break;
462         default:
463             pr2serr("unrecognised option code 0x%x ??\n", c);
464             usage();
465             return SG_LIB_SYNTAX_ERROR;
466         }
467     }
468     if (optind < argc) {
469         if (NULL == device_name) {
470             device_name = argv[optind];
471             ++optind;
472         }
473         if (optind < argc) {
474             for (; optind < argc; ++optind)
475                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
476             usage();
477             return SG_LIB_SYNTAX_ERROR;
478         }
479     }
480 #ifdef DEBUG
481     pr2serr("In DEBUG mode, ");
482     if (verbose_given && version_given) {
483         pr2serr("but override: '-vV' given, zero verbose and continue\n");
484         verbose_given = false;
485         version_given = false;
486         verbose = 0;
487     } else if (! verbose_given) {
488         pr2serr("set '-vv'\n");
489         verbose = 2;
490     } else
491         pr2serr("keep verbose=%d\n", verbose);
492 #else
493     if (verbose_given && version_given)
494         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
495 #endif
496     if (version_given) {
497         pr2serr("version: %s\n", version_str);
498         return 0;
499     }
500 
501     if (test_arg) {
502         memset(lun_arr, 0, sizeof(lun_arr));
503         cp = test_arg;
504         /* check for leading 'L' */
505 #ifdef SG_LIB_LINUX
506         if ('L' == toupper(cp[0])) {
507             uint64_t ull;
508 
509             if (('0' == cp[1]) && ('X' == toupper((uint8_t)cp[2])))
510                 k = sscanf(cp + 3, " %" SCNx64, &ull);
511             else
512                 k = sscanf(cp + 1, " %" SCNu64, &ull);
513             if (1 != k) {
514                 pr2serr("Unable to read Linux style LUN integer given to "
515                         "--test=\n");
516                 return SG_LIB_SYNTAX_ERROR;
517             }
518             linux2t10_lun(ull, lun_arr);
519             test_linux_in = true;
520         } else
521 #endif
522         {
523             /* Check if trailing 'L' */
524 #ifdef SG_LIB_LINUX
525             m = strlen(cp);    /* must be at least 1 char in test_arg */
526             if ('L' == toupper(cp[m - 1]))
527                 test_linux_out = true;
528 #endif
529             if (('0' == cp[0]) && ('X' == toupper(cp[1])))
530                 cp += 2;
531             if (strchr(cp, ' ') || strchr(cp, '\t') || strchr(cp, '-')) {
532                 for (k = 0; k < 8; ++k, cp += 2) {
533                     c = *cp;
534                     if ('\0' == c)
535                         break;
536                     else if (! isxdigit(c))
537                         ++cp;
538                     if (1 != sscanf(cp, "%2x", &h))
539                         break;
540                     lun_arr[k] = h & 0xff;
541                 }
542             } else {
543                 for (k = 0; k < 8; ++k, cp += 2) {
544                 if (1 != sscanf(cp, "%2x", &h))
545                         break;
546                     lun_arr[k] = h & 0xff;
547                 }
548             }
549             if (0 == k) {
550                 pr2serr("expected a hex number, optionally prefixed by "
551                         "'0x'\n");
552                 return SG_LIB_SYNTAX_ERROR;
553             }
554         }
555 #ifdef SG_LIB_LINUX
556         if (verbose || test_linux_in || decode_arg)
557 #else
558         if (verbose || decode_arg)
559 #endif
560         {
561             if (decode_arg > 1) {
562                 printf("64 bit LUN in T10 (hex, dashed) format: ");
563                 for (k = 0; k < 8; k += 2)
564                     printf("%c%02x%02x", (k ? '-' : ' '), lun_arr[k],
565                            lun_arr[k + 1]);
566             } else {
567                 printf("64 bit LUN in T10 preferred (hex) format: ");
568                 for (k = 0; k < 8; ++k)
569                     printf(" %02x", lun_arr[k]);
570             }
571             printf("\n");
572         }
573 #ifdef SG_LIB_LINUX
574         if (test_linux_out) {
575             if (do_hex > 1)
576                 printf("Linux 'word flipped' integer LUN representation: "
577                        "0x%016" PRIx64 "\n", t10_2linux_lun(lun_arr));
578             else if (do_hex)
579                 printf("Linux 'word flipped' integer LUN representation: 0x%"
580                        PRIx64 "\n", t10_2linux_lun(lun_arr));
581             else
582                 printf("Linux 'word flipped' integer LUN representation: %"
583                        PRIu64 "\n", t10_2linux_lun(lun_arr));
584         }
585 #endif
586         printf("Decoded LUN:\n");
587         decode_lun("  ", lun_arr, (lu_cong_arg % 2), do_hex, verbose);
588         return 0;
589     }
590     if (NULL == device_name) {
591         pr2serr("missing device name!\n");
592         usage();
593         return SG_LIB_SYNTAX_ERROR;
594     }
595 
596     if (do_raw) {
597         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
598             perror("sg_set_binary_mode");
599             return SG_LIB_FILE_ERROR;
600         }
601     }
602 
603     sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
604     if (sg_fd < 0) {
605         int err = -sg_fd;
606 
607         pr2serr("open error: %s: %s\n", device_name, safe_strerror(err));
608         if ((! o_readonly) && ((err == EACCES) || (err == EROFS)))
609             pr2serr("Perhaps try again with --readonly option or with root "
610                     "permissions\n");
611         return sg_convert_errno(-sg_fd);
612     }
613     if (decode_arg && (! lu_cong_arg_given)) {
614         if (verbose > 1)
615             pr2serr("in order to decode LUN and since --lu_cong not given, "
616                     "do standard\nINQUIRY to find LU_CONG bit\n");
617         /* check if LU_CONG set in standard INQUIRY response */
618         res = sg_simple_inquiry(sg_fd, &sir, false, verbose);
619         ret = res;
620         if (res) {
621             pr2serr("fetching standard INQUIRY response failed\n");
622             goto the_end;
623         }
624         lu_cong_arg = !!(0x40 & sir.byte_1);
625         if (verbose && lu_cong_arg)
626             pr2serr("LU_CONG bit set in standard INQUIRY response\n");
627     }
628 
629     if (0 == maxlen)
630         maxlen = DEF_RLUNS_BUFF_LEN;
631     reportLunsBuff = (uint8_t *)sg_memalign(maxlen, 0, &free_reportLunsBuff,
632                                             verbose > 3);
633     if (NULL == reportLunsBuff) {
634         pr2serr("unable to sg_memalign %d bytes\n", maxlen);
635         return sg_convert_errno(ENOMEM);
636     }
637     trunc = false;
638 
639     res = sg_ll_report_luns(sg_fd, select_rep, reportLunsBuff, maxlen, true,
640                             verbose);
641     ret = res;
642     if (0 == res) {
643         list_len = sg_get_unaligned_be32(reportLunsBuff + 0);
644         len_cap = list_len + 8;
645         if (len_cap > maxlen)
646             len_cap = maxlen;
647         if (do_raw) {
648             dStrRaw((const char *)reportLunsBuff, len_cap);
649             goto the_end;
650         }
651         if (1 == do_hex) {
652             hex2stdout(reportLunsBuff, len_cap, 1);
653             goto the_end;
654         }
655         luns = (list_len / 8);
656         if (! do_quiet)
657             printf("Lun list length = %d which imples %d lun entr%s\n",
658                    list_len, luns, ((1 == luns) ? "y" : "ies"));
659         if ((list_len + 8) > maxlen) {
660             luns = ((maxlen - 8) / 8);
661             trunc = true;
662             pr2serr("  <<too many luns for internal buffer, will show %d "
663                     "lun%s>>\n", luns, ((1 == luns) ? "" : "s"));
664         }
665         if (verbose > 1) {
666             pr2serr("\nOutput response in hex\n");
667             hex2stderr(reportLunsBuff, (trunc ? maxlen : list_len + 8), 1);
668         }
669         for (k = 0, off = 8; k < luns; ++k, off += 8) {
670             if (! do_quiet) {
671                 if (0 == k)
672                     printf("Report luns [select_report=0x%x]:\n", select_rep);
673                 printf("    ");
674             }
675             for (m = 0; m < 8; ++m)
676                 printf("%02x", reportLunsBuff[off + m]);
677 #ifdef SG_LIB_LINUX
678             if (do_linux) {
679                 uint64_t lin_lun;
680 
681                 lin_lun = t10_2linux_lun(reportLunsBuff + off);
682                 if (do_hex > 1)
683                     printf("    [0x%" PRIx64 "]", lin_lun);
684                 else
685                     printf("    [%" PRIu64 "]", lin_lun);
686             }
687 #endif
688             printf("\n");
689             if (decode_arg)
690                 decode_lun("      ", reportLunsBuff + off,
691                            (bool)(lu_cong_arg % 2), do_hex, verbose);
692         }
693     } else if (SG_LIB_CAT_INVALID_OP == res)
694         pr2serr("Report Luns command not supported (support mandatory in "
695                 "SPC-3)\n");
696     else if (SG_LIB_CAT_ABORTED_COMMAND == res)
697         pr2serr("Report Luns, aborted command\n");
698     else if (SG_LIB_CAT_ILLEGAL_REQ == res)
699         pr2serr("Report Luns command has bad field in cdb\n");
700     else {
701         char b[80];
702 
703         sg_get_category_sense_str(res, sizeof(b), b, verbose);
704         pr2serr("Report Luns command: %s\n", b);
705     }
706 
707 the_end:
708     if (free_reportLunsBuff)
709         free(free_reportLunsBuff);
710     res = sg_cmds_close_device(sg_fd);
711     if (res < 0) {
712         pr2serr("close error: %s\n", safe_strerror(-res));
713         if (0 == ret)
714             return sg_convert_errno(-res);
715     }
716     if (0 == verbose) {
717         if (! sg_if_can2stderr("sg_luns failed: ", ret))
718             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
719                     "more information\n");
720     }
721     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
722 }
723