xref: /aosp_15_r20/external/sg3_utils/src/sg_referrals.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2010-2018 Hannes Reinecke.
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 
26 #include "sg_lib.h"
27 #include "sg_cmds_basic.h"
28 #include "sg_cmds_extra.h"
29 #include "sg_unaligned.h"
30 #include "sg_pr2serr.h"
31 
32 /*
33  * A utility program originally written for the Linux OS SCSI subsystem.
34  *
35  *
36  * This program issues the SCSI REPORT REFERRALS command to the given
37  * SCSI device.
38  */
39 
40 static const char * version_str = "1.13 20180628";    /* sbc4r10 */
41 
42 #define MAX_REFER_BUFF_LEN (1024 * 1024)
43 #define DEF_REFER_BUFF_LEN 256
44 
45 #define TPGS_STATE_OPTIMIZED 0x0
46 #define TPGS_STATE_NONOPTIMIZED 0x1
47 #define TPGS_STATE_STANDBY 0x2
48 #define TPGS_STATE_UNAVAILABLE 0x3
49 #define TPGS_STATE_LB_DEPENDENT 0x4
50 #define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
51 #define TPGS_STATE_TRANSITIONING 0xf
52 
53 static uint8_t referralBuff[DEF_REFER_BUFF_LEN];
54 
decode_tpgs_state(const int st)55 static const char *decode_tpgs_state(const int st)
56 {
57     switch (st) {
58     case TPGS_STATE_OPTIMIZED:
59         return "active/optimized";
60         break;
61     case TPGS_STATE_NONOPTIMIZED:
62         return "active/non optimized";
63         break;
64     case TPGS_STATE_STANDBY:
65         return "standby";
66         break;
67     case TPGS_STATE_UNAVAILABLE:
68         return "unavailable";
69         break;
70     case TPGS_STATE_LB_DEPENDENT:
71         return "logical block dependent";
72         break;
73     case TPGS_STATE_OFFLINE:
74         return "offline";
75         break;
76     case TPGS_STATE_TRANSITIONING:
77         return "transitioning between states";
78         break;
79     default:
80         return "unknown";
81         break;
82     }
83 }
84 
85 static struct option long_options[] = {
86         {"help", no_argument, 0, 'h'},
87         {"hex", no_argument, 0, 'H'},
88         {"lba", required_argument, 0, 'l'},
89         {"maxlen", required_argument, 0, 'm'},
90         {"one-segment", no_argument, 0, 's'},
91         {"one_segment", no_argument, 0, 's'},
92         {"raw", no_argument, 0, 'r'},
93         {"readonly", no_argument, 0, 'R'},
94         {"verbose", no_argument, 0, 'v'},
95         {"version", no_argument, 0, 'V'},
96         {0, 0, 0, 0},
97 };
98 
99 static void
usage()100 usage()
101 {
102     pr2serr("Usage: sg_referrals  [--help] [--hex] [--lba=LBA] "
103             "[--maxlen=LEN]\n"
104             "                     [--one-segment] [--raw] [--readonly] "
105             "[--verbose]\n"
106             "                     [--version] DEVICE\n"
107             "  where:\n"
108             "    --help|-h         print out usage message\n"
109             "    --hex|-H          output in hexadecimal\n"
110             "    --lba=LBA|-l LBA    starting LBA (logical block address) "
111             "(def: 0)\n"
112             "    --maxlen=LEN|-m LEN    max response length (allocation "
113             "length in cdb)\n"
114             "                           (def: 0 -> %d bytes)\n",
115             DEF_REFER_BUFF_LEN );
116     pr2serr("    --one-segment|-s    return information about the specified "
117             "segment only\n"
118             "    --raw|-r          output in binary\n"
119             "    --verbose|-v      increase verbosity\n"
120             "    --version|-V      print version string and exit\n\n"
121             "Performs a SCSI REPORT REFERRALS command (SBC-3)\n"
122             );
123 }
124 
125 static void
dStrRaw(const uint8_t * str,int len)126 dStrRaw(const uint8_t * str, int len)
127 {
128     int k;
129 
130     for (k = 0; k < len; ++k)
131         printf("%c", str[k]);
132 }
133 
134 /* Decodes given user data referral segment descriptor
135  * the number of blocks and returns the number of bytes processed,
136  * -1 for error.
137  */
138 static int
decode_referral_desc(const uint8_t * bp,int bytes)139 decode_referral_desc(const uint8_t * bp, int bytes)
140 {
141     int j, n;
142     uint64_t first, last;
143 
144     if (NULL == bp)
145         return -1;
146 
147     if (bytes < 20)
148         return -1;
149 
150     first = sg_get_unaligned_be64(bp + 4);
151     last = sg_get_unaligned_be64(bp + 12);
152 
153     printf("    target port descriptors: %d\n", bp[3]);
154     printf("    user data segment: first lba %" PRIu64 ", last lba %"
155           PRIu64 "\n", first, last);
156     n = 20;
157     bytes -= n;
158     for (j = 0; j < bp[3]; j++) {
159         if (bytes < 4)
160             return -1;
161         printf("      target port descriptor %d:\n", j);
162         printf("        port group %x state (%s)\n",
163                sg_get_unaligned_be16(bp + n + 2),
164                decode_tpgs_state(bp[n] & 0xf));
165         n += 4;
166         bytes -= 4;
167     }
168     return n;
169 }
170 
171 
172 int
main(int argc,char * argv[])173 main(int argc, char * argv[])
174 {
175     bool do_one_segment = false;
176     bool o_readonly = false;
177     bool do_raw = false;
178     bool verbose_given = false;
179     bool version_given = false;
180     int k, res, c, rlen;
181     int sg_fd = -1;
182     int do_hex = 0;
183     int maxlen = DEF_REFER_BUFF_LEN;
184     int verbose = 0;
185     int desc = 0;
186     int ret = 0;
187     int64_t ll;
188     uint64_t lba = 0;
189     const char * device_name = NULL;
190     const uint8_t * bp;
191     uint8_t * referralBuffp = referralBuff;
192     uint8_t * free_referralBuffp = NULL;
193 
194     while (1) {
195         int option_index = 0;
196 
197         c = getopt_long(argc, argv, "hHl:m:rRsvV", long_options,
198                         &option_index);
199         if (c == -1)
200             break;
201 
202         switch (c) {
203         case 'h':
204         case '?':
205             usage();
206             return 0;
207         case 'H':
208             ++do_hex;
209             break;
210         case 'l':
211             ll = sg_get_llnum(optarg);
212             if (-1 == ll) {
213                 pr2serr("bad argument to '--lba'\n");
214                 return SG_LIB_SYNTAX_ERROR;
215             }
216             lba = (uint64_t)ll;
217             break;
218         case 'm':
219             maxlen = sg_get_num(optarg);
220             if ((maxlen < 0) || (maxlen > MAX_REFER_BUFF_LEN)) {
221                 pr2serr("argument to '--maxlen' should be %d or less\n",
222                         MAX_REFER_BUFF_LEN);
223                 return SG_LIB_SYNTAX_ERROR;
224             }
225             break;
226         case 's':
227             do_one_segment = true;
228             break;
229         case 'r':
230             do_raw = true;
231             break;
232         case 'R':
233             o_readonly = true;
234             break;
235         case 'v':
236             verbose_given = true;
237             ++verbose;
238             break;
239         case 'V':
240             version_given = true;
241             break;
242         default:
243             pr2serr("unrecognised option code 0x%x ??\n", c);
244             usage();
245             return SG_LIB_SYNTAX_ERROR;
246         }
247     }
248     if (optind < argc) {
249         if (NULL == device_name) {
250             device_name = argv[optind];
251             ++optind;
252         }
253         if (optind < argc) {
254             for (; optind < argc; ++optind)
255                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
256             usage();
257             return SG_LIB_SYNTAX_ERROR;
258         }
259     }
260 #ifdef DEBUG
261     pr2serr("In DEBUG mode, ");
262     if (verbose_given && version_given) {
263         pr2serr("but override: '-vV' given, zero verbose and continue\n");
264         verbose_given = false;
265         version_given = false;
266         verbose = 0;
267     } else if (! verbose_given) {
268         pr2serr("set '-vv'\n");
269         verbose = 2;
270     } else
271         pr2serr("keep verbose=%d\n", verbose);
272 #else
273     if (verbose_given && version_given)
274         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
275 #endif
276     if (version_given) {
277         pr2serr("version: %s\n", version_str);
278         return 0;
279     }
280 
281     if (NULL == device_name) {
282         pr2serr("No DEVICE argument given\n\n");
283         usage();
284         return SG_LIB_SYNTAX_ERROR;
285     }
286     if (maxlen > DEF_REFER_BUFF_LEN) {
287         referralBuffp = (uint8_t *)sg_memalign(maxlen, 0,
288                                                &free_referralBuffp,
289                                                verbose > 3);
290         if (NULL == referralBuffp) {
291             pr2serr("unable to allocate %d bytes on heap\n", maxlen);
292             return sg_convert_errno(ENOMEM);
293         }
294     }
295     if (do_raw) {
296         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
297             perror("sg_set_binary_mode");
298             ret = SG_LIB_FILE_ERROR;
299             goto free_buff;
300         }
301     }
302 
303     sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
304     if (sg_fd < 0) {
305         if (verbose)
306             pr2serr("open error: %s: %s\n", device_name,
307                     safe_strerror(-sg_fd));
308         ret = sg_convert_errno(-sg_fd);
309         goto free_buff;
310     }
311 
312     res = sg_ll_report_referrals(sg_fd, lba, do_one_segment, referralBuffp,
313                                  maxlen, true, verbose);
314     ret = res;
315     if (0 == res) {
316         if (maxlen >= 4)
317             /*
318              * This is strictly speaking incorrect. However, the
319              * spec reserved bytes 0 and 1, so some implementations
320              * might want to use them to increase the number of
321              * possible user segments.
322              * And maybe someone takes a pity and updates the spec ...
323              */
324             rlen = sg_get_unaligned_be32(referralBuffp + 0) + 4;
325         else
326             rlen = maxlen;
327         k = (rlen > maxlen) ? maxlen : rlen;
328         if (do_raw) {
329             dStrRaw(referralBuffp, k);
330             goto the_end;
331         }
332         if (do_hex) {
333             hex2stdout(referralBuffp, k, 1);
334             goto the_end;
335         }
336         if (maxlen < 4) {
337             if (verbose)
338                 pr2serr("Exiting because allocation length (maxlen)  less "
339                         "than 4\n");
340             goto the_end;
341         }
342         if ((verbose > 1) || (verbose && (rlen > maxlen))) {
343             pr2serr("response length %d bytes\n", rlen);
344             if (rlen > maxlen)
345                 pr2serr("  ... which is greater than maxlen (allocation "
346                         "length %d), truncation\n", maxlen);
347         }
348         if (rlen > maxlen)
349             rlen = maxlen;
350 
351         bp = referralBuffp + 4;
352         k = 0;
353         printf("Report referrals:\n");
354         while (k < rlen - 4) {
355             printf("  descriptor %d:\n", desc);
356             res = decode_referral_desc(bp + k, rlen - 4 - k);
357             if (res < 0) {
358                 pr2serr("bad user data segment referral descriptor\n");
359                 break;
360             }
361             k += res;
362             desc++;
363         }
364     } else {
365         char b[80];
366 
367         sg_get_category_sense_str(res, sizeof(b), b, verbose);
368         pr2serr("Report Referrals command failed: %s\n", b);
369     }
370 
371 the_end:
372     res = sg_cmds_close_device(sg_fd);
373     if (res < 0) {
374         pr2serr("close error: %s\n", safe_strerror(-res));
375         if (0 == ret)
376             ret = sg_convert_errno(-res);
377     }
378 free_buff:
379     if (free_referralBuffp)
380         free(free_referralBuffp);
381     if (0 == verbose) {
382         if (! sg_if_can2stderr("sg_referrals failed: ", ret))
383             pr2serr("Some error occurred, try again with '-v' "
384                     "or '-vv' for more information\n");
385     }
386     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
387 }
388