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 <getopt.h>
18 #define __STDC_FORMAT_MACROS 1
19 #include <inttypes.h>
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
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 READ BLOCK LIMITS command (SSC) to the given
35 * SCSI device.
36 */
37
38 static const char * version_str = "1.09 20221101";
39
40 #define DEF_READ_BLOCK_LIMITS_LEN 6
41 #define MLIO_READ_BLOCK_LIMITS_LEN 20
42 #define MAX_READ_BLOCK_LIMITS_LEN MLIO_READ_BLOCK_LIMITS_LEN
43
44 static uint8_t readBlkLmtBuff[MAX_READ_BLOCK_LIMITS_LEN];
45
46
47 static struct option long_options[] = {
48 {"help", no_argument, 0, 'h'},
49 {"hex", no_argument, 0, 'H'},
50 {"mloi", no_argument, 0, 'm'}, /* added in ssc4r02.pdf */
51 {"raw", no_argument, 0, 'r'},
52 {"readonly", no_argument, 0, 'R'},
53 {"verbose", no_argument, 0, 'v'},
54 {"version", no_argument, 0, 'V'},
55 {0, 0, 0, 0},
56 };
57
58
59 static void
usage()60 usage()
61 {
62 pr2serr("Usage: sg_read_block_limits [--help] [--hex] [--mloi] "
63 "[--raw]\n"
64 " [--readonly] [--verbose] "
65 "[--version]\n"
66 " DEVICE\n"
67 " where:\n"
68 " --help|-h print out usage message\n"
69 " --hex|-H output response in hexadecimal\n"
70 " --mloi|-m output maximum logical object "
71 "identifier\n"
72 " --raw|-r output response in binary to stdout\n"
73 " --readonly|-R open DEVICE in read-only mode\n"
74 " --verbose|-v increase verbosity\n"
75 " --version|-V print version string and exit\n\n"
76 "Performs a SCSI READ BLOCK LIMITS command and decode the "
77 "response\n"
78 );
79 }
80
81 static void
dStrRaw(const char * str,int len)82 dStrRaw(const char * str, int len)
83 {
84 int k;
85
86 for (k = 0; k < len; ++k)
87 printf("%c", str[k]);
88 }
89
90 int
main(int argc,char * argv[])91 main(int argc, char * argv[])
92 {
93 bool do_mloi = false;
94 bool do_raw = false;
95 bool readonly = false;
96 bool verbose_given = false;
97 bool version_given = false;
98 int sg_fd, k, m, res, c, max_resp_len;
99 int resid = 0;
100 int actual_len = 0;
101 int do_hex = 0;
102 int verbose = 0;
103 int ret = 0;
104 uint32_t max_block_size;
105 uint64_t mloi;
106 uint16_t min_block_size;
107 uint8_t granularity;
108 const char * device_name = NULL;
109
110 while (1) {
111 int option_index = 0;
112
113 c = getopt_long(argc, argv, "hHmrRvV", long_options,
114 &option_index);
115 if (c == -1)
116 break;
117
118 switch (c) {
119 case 'h':
120 case '?':
121 usage();
122 return 0;
123 case 'H':
124 ++do_hex;
125 break;
126 case 'm':
127 do_mloi = true;
128 break;
129 case 'r':
130 do_raw = true;
131 break;
132 case 'R':
133 readonly = true;
134 break;
135 case 'v':
136 verbose_given = true;
137 ++verbose;
138 break;
139 case 'V':
140 version_given = true;
141 break;
142 default:
143 pr2serr("invalid option -%c ??\n", c);
144 usage();
145 return SG_LIB_SYNTAX_ERROR;
146 }
147 }
148 if (optind < argc) {
149 if (NULL == device_name) {
150 device_name = argv[optind];
151 ++optind;
152 }
153 if (optind < argc) {
154 for (; optind < argc; ++optind)
155 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
156 usage();
157 return SG_LIB_SYNTAX_ERROR;
158 }
159 }
160 #ifdef DEBUG
161 pr2serr("In DEBUG mode, ");
162 if (verbose_given && version_given) {
163 pr2serr("but override: '-vV' given, zero verbose and continue\n");
164 verbose_given = false;
165 version_given = false;
166 verbose = 0;
167 } else if (! verbose_given) {
168 pr2serr("set '-vv'\n");
169 verbose = 2;
170 } else
171 pr2serr("keep verbose=%d\n", verbose);
172 #else
173 if (verbose_given && version_given)
174 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
175 #endif
176 if (version_given) {
177 printf("version: %s\n", version_str);
178 return 0;
179 }
180
181 if (NULL == device_name) {
182 pr2serr("missing device name!\n");
183 usage();
184 return SG_LIB_SYNTAX_ERROR;
185 }
186
187 sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
188 if (sg_fd < 0) {
189 if (verbose)
190 pr2serr("open error: %s: %s\n", device_name,
191 safe_strerror(-sg_fd));
192 ret = sg_convert_errno(-sg_fd);
193 goto the_end2;
194 }
195
196 max_resp_len = do_mloi ? MLIO_READ_BLOCK_LIMITS_LEN :
197 DEF_READ_BLOCK_LIMITS_LEN;
198 memset(readBlkLmtBuff, 0x0, sizeof(readBlkLmtBuff));
199 res = sg_ll_read_block_limits_v2(sg_fd, do_mloi, readBlkLmtBuff,
200 max_resp_len, &resid, true, verbose);
201 ret = res;
202 if (0 == res) {
203 actual_len = max_resp_len - resid;
204 if (do_hex) {
205 int fl = -1;
206
207 if (1 == do_hex)
208 fl = 1;
209 else if (2 == do_hex)
210 fl = 0;
211 hex2stdout(readBlkLmtBuff, actual_len, fl);
212 goto the_end;
213 } else if (do_raw) {
214 dStrRaw((const char *)readBlkLmtBuff, actual_len);
215 goto the_end;
216 }
217
218 if (do_mloi) {
219 if (actual_len < MLIO_READ_BLOCK_LIMITS_LEN) {
220 pr2serr("Expected at least %d bytes in response but only "
221 "%d bytes\n", MLIO_READ_BLOCK_LIMITS_LEN, actual_len);
222 goto the_end;
223 }
224 printf("Read Block Limits (MLOI=1) results:\n");
225 mloi = sg_get_unaligned_be64(readBlkLmtBuff + 12);
226 printf(" Maximum logical block identifier: %" PRIu64 "\n",
227 mloi);
228 } else { /* MLOI=0 (only case before ssc4r02.pdf) */
229 if (actual_len < DEF_READ_BLOCK_LIMITS_LEN) {
230 pr2serr("Expected at least %d bytes in response but only "
231 "%d bytes\n", DEF_READ_BLOCK_LIMITS_LEN, actual_len);
232 goto the_end;
233 }
234 max_block_size = sg_get_unaligned_be32(readBlkLmtBuff + 0);
235 // first byte contains granularity field
236 granularity = (max_block_size >> 24) & 0x1F;
237 max_block_size = max_block_size & 0xFFFFFF;
238 min_block_size = sg_get_unaligned_be16(readBlkLmtBuff + 4);
239 k = min_block_size / 1024;
240 printf("Read Block Limits results:\n");
241 printf(" Minimum block size: %u byte(s)",
242 (unsigned int)min_block_size);
243 if (k != 0)
244 printf(", %d KB", k);
245 printf("\n");
246 k = max_block_size / 1024;
247 m = max_block_size / 1048576;
248 printf(" Maximum block size: %u byte(s)",
249 (unsigned int)max_block_size);
250 if (k != 0)
251 printf(", %d KB", k);
252 if (m != 0)
253 printf(", %d MB", m);
254 printf("\n");
255 printf(" Granularity: %u",
256 (unsigned int)granularity);
257 printf("\n");
258 }
259 } else { /* error detected */
260 char b[80];
261
262 sg_get_category_sense_str(res, sizeof(b), b, verbose);
263 pr2serr("Read block limits: %s\n", b);
264 if (0 == verbose)
265 pr2serr(" try '-v' option for more information\n");
266 }
267
268 the_end:
269 res = sg_cmds_close_device(sg_fd);
270 if (res < 0) {
271 pr2serr("close error: %s\n", safe_strerror(-res));
272 if (0 == ret)
273 ret = sg_convert_errno(-res);
274 }
275 the_end2:
276 if (0 == verbose) {
277 if (! sg_if_can2stderr("sg_read_block_limits failed: ", ret))
278 pr2serr("Some error occurred, try again with '-v' or '-vv' for "
279 "more information\n");
280 }
281 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
282 }
283