1 /*
2 * Copyright (c) 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 <ctype.h>
19 #include <getopt.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "sg_lib.h"
28 #include "sg_lib_data.h"
29 #include "sg_pt.h"
30 #include "sg_cmds_basic.h"
31 #include "sg_unaligned.h"
32 #include "sg_pr2serr.h"
33
34 /* A utility program originally written for the Linux OS SCSI subsystem.
35 *
36 *
37 * This program issues the SCSI REPORT DENSITY SUPPORT command to the given
38 * SCSI (tape) device and outputs the response. Based on ssc5r06.pdf
39 */
40
41 static const char * version_str = "1.00 20220120";
42
43 #define MAX_RDS_BUFF_LEN (64 * 1024 - 1)
44 #define DEF_RDS_BUFF_LEN 4096
45
46 #define REPORT_DENSITY_SUPPORT_CMD 0x44
47 #define REPORT_DENSITY_SUPPORT_CMDLEN 10
48
49 #define RDS_DENSITY_DESC_LEN 52
50 #define RDS_MEDIUM_T_DESC_LEN 56
51
52 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
53 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
54
55 static const char * rds_s = "Report density support";
56
57
58 static struct option long_options[] = {
59 {"help", no_argument, 0, 'h'},
60 {"hex", no_argument, 0, 'H'},
61 {"in", required_argument, 0, 'i'}, /* silent, same as --inhex= */
62 {"inhex", required_argument, 0, 'i'},
63 {"maxlen", required_argument, 0, 'm'},
64 {"media", no_argument, 0, 'M'}, /* Media field; byte 1, bit 0 */
65 {"raw", no_argument, 0, 'r'},
66 {"readonly", no_argument, 0, 'R'},
67 {"typem", no_argument, 0, 't'}, /* Medium type field, byte 1, bit 1 */
68 {"verbose", no_argument, 0, 'v'},
69 {"version", no_argument, 0, 'V'},
70 {0, 0, 0, 0},
71 };
72
73
74 static void
usage(void)75 usage(void)
76 {
77 pr2serr("Usage: "
78 "sg_rep_density [--help] [--hex] [--inhex=FN] [--maxlen=LEN] "
79 "[--media]\n"
80 " [--raw] [--readonly] [--typem] [--verbose] "
81 "[--version]\n"
82 " DEVICE\n");
83 pr2serr(" where:\n"
84 " --help|-h prints out this usage message\n"
85 " --hex|-H output response in hexadecimal "
86 "(default); used\n"
87 " twice: hex without addresses at start "
88 "of line\n"
89 " --inhex=FN decode contents of FN, ignore DEVICE\n"
90 " --maxlen=LEN|-m LEN max response length (allocation "
91 "length in cdb)\n"
92 " (def: 512 bytes)\n"
93 " --media|-M report on media in drive (def: report "
94 "on drive)\n"
95 " --raw|-r output response in binary\n"
96 " --readonly|-R open DEVICE read-only (def: read-write)\n"
97 " --typem|-t report medium types (def: density "
98 "codes)\n"
99 " --verbose|-v increase verbosity\n"
100 " --version|-V print version string and exit\n\n"
101 "Sends a SCSI REPORT DENSITY SUPPORT command outputs the "
102 "response in\nASCII hexadecimal or binary. By default it reports "
103 "on density codes supported\nby the drive (LU).\n");
104 }
105
106 /* Invokes a SCSI REPORT PROVISIONING INITIALIZATION PATTERN command (SBC).
107 * Return of 0 -> success, various SG_LIB_CAT_* positive values or
108 * -1 -> other errors */
109 static int
sg_ll_report_density(int sg_fd,bool media,bool m_type,void * resp,int mx_resp_len,int * residp,bool noisy,int verbose)110 sg_ll_report_density(int sg_fd, bool media, bool m_type, void * resp,
111 int mx_resp_len, int * residp, bool noisy, int verbose)
112 {
113 int ret, res, sense_cat;
114 uint8_t rds_cdb[REPORT_DENSITY_SUPPORT_CMDLEN] =
115 {REPORT_DENSITY_SUPPORT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
116 uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
117 struct sg_pt_base * ptvp;
118
119 if (media)
120 rds_cdb[1] |= 0x1;
121 if (m_type)
122 rds_cdb[1] |= 0x2;
123 sg_put_unaligned_be16((uint16_t)mx_resp_len, rds_cdb + 7);
124 if (verbose) {
125 char b[128];
126
127 pr2serr(" %s cdb: %s\n", rds_s,
128 sg_get_command_str(rds_cdb, REPORT_DENSITY_SUPPORT_CMDLEN,
129 false, sizeof(b), b));
130 }
131 ptvp = construct_scsi_pt_obj();
132 if (NULL == ptvp) {
133 pr2serr("%s: out of memory\n", __func__);
134 return -1;
135 }
136 set_scsi_pt_cdb(ptvp, rds_cdb, sizeof(rds_cdb));
137 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
138 set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
139 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
140 ret = sg_cmds_process_resp(ptvp, rds_s, res, noisy, verbose, &sense_cat);
141 if (-1 == ret) {
142 if (get_scsi_pt_transport_err(ptvp))
143 ret = SG_LIB_TRANSPORT_ERROR;
144 else
145 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
146 } else if (-2 == ret) {
147 switch (sense_cat) {
148 case SG_LIB_CAT_RECOVERED:
149 case SG_LIB_CAT_NO_SENSE:
150 ret = 0;
151 break;
152 default:
153 ret = sense_cat;
154 break;
155 }
156 } else
157 ret = 0;
158 if (residp)
159 *residp = get_scsi_pt_resid(ptvp);
160 destruct_scsi_pt_obj(ptvp);
161 return ret;
162 }
163
164 static void
decode_medium_type(const uint8_t * up,int num_desc)165 decode_medium_type(const uint8_t * up, int num_desc)
166 {
167 int k, j, n, q;
168
169 for (k = 0; k < num_desc; ++k, up += RDS_MEDIUM_T_DESC_LEN) {
170 if (0 == k)
171 printf("Medium type descriptor%s\n", ((num_desc > 1) ? "s" : ""));
172 printf(" descriptor %d\n", k + 1);
173 printf(" Medium type: %u\n", up[0]);
174 n = up[4];
175 printf(" Number of density codes: %d\n", n);
176 if (n > 9)
177 n = 9;
178 for (j = 0; j < n; ++j) {
179 q = up[5 + j];
180 if (q > 0)
181 printf(" Primary density code: %d\n", q);
182 }
183 printf(" Media width: %u\n", sg_get_unaligned_be16(up + 14));
184 printf(" Medium length: %u\n", sg_get_unaligned_be16(up + 16));
185 printf(" Assigning organization: %.8s\n", (const char *)(up + 20));
186 printf(" Medium type name: %.8s\n", (const char *)(up + 28));
187 printf(" Description: %.20s\n", (const char *)(up + 36));
188 }
189 }
190
191 static void
decode_density_code(const uint8_t * up,int num_desc)192 decode_density_code(const uint8_t * up, int num_desc)
193 {
194 int k;
195
196 for (k = 0; k < num_desc; ++k, up += RDS_DENSITY_DESC_LEN) {
197 if (0 == k)
198 printf("Density support data block descriptor%s\n",
199 ((num_desc > 1) ? "s" : ""));
200 printf(" descriptor %d\n", k + 1);
201 printf(" Primary density code: %u\n", up[0]);
202 printf(" Secondary density code: %u\n", up[1]);
203 printf(" WRT: %u\n", !!(0x80 & up[2]));
204 printf(" DUP: %u\n", !!(0x40 & up[2]));
205 printf(" DEFLT: %u\n", !!(0x20 & up[2]));
206 printf(" DLV: %u\n", !!(0x1 & up[2]));
207 printf(" Bits per mm: %u\n", sg_get_unaligned_be24(up + 5));
208 printf(" Media width: %u\n", sg_get_unaligned_be16(up + 8));
209 printf(" Tracks: %u\n", sg_get_unaligned_be16(up + 10));
210 printf(" Capacity: %u\n", sg_get_unaligned_be32(up + 12));
211 printf(" Assigning organization: %.8s\n", (const char *)(up + 16));
212 printf(" Density name: %.8s\n", (const char *)(up + 24));
213 printf(" Description: %.20s\n", (const char *)(up + 32));
214 }
215 }
216
217 static void
dStrRaw(const uint8_t * str,int len)218 dStrRaw(const uint8_t * str, int len)
219 {
220 int k;
221
222 for (k = 0; k < len; ++k)
223 printf("%c", str[k]);
224 }
225
226
227 int
main(int argc,char * argv[])228 main(int argc, char * argv[])
229 {
230 bool do_raw = false;
231 bool media = false;
232 bool m_type = false;
233 bool no_final_msg = false;
234 bool o_readonly = false;
235 bool verbose_given = false;
236 bool version_given = false;
237 int res, c, rlen, desc_len, ads_len, num_desc;
238 int resid = 0;
239 int sg_fd = -1;
240 int do_help = 0;
241 int do_hex = 0;
242 int maxlen = 0;
243 int in_len = 0;
244 int ret = 0;
245 int verbose = 0;
246 const char * device_name = NULL;
247 const char * inhex_fn = NULL;
248 uint8_t * rdsBuff = NULL;
249 uint8_t * free_rds = NULL;
250 char b[80];
251
252 while (1) {
253 int option_index = 0;
254
255 c = getopt_long(argc, argv, "hHi:m:MrRtvV", long_options,
256 &option_index);
257 if (c == -1)
258 break;
259
260 switch (c) {
261 case 'h':
262 case '?':
263 ++do_help;
264 break;
265 case 'H':
266 ++do_hex;
267 break;
268 case 'i':
269 inhex_fn = optarg;
270 break;
271 case 'm':
272 maxlen = sg_get_num(optarg);
273 if ((maxlen < 0) || (maxlen > MAX_RDS_BUFF_LEN)) {
274 pr2serr("argument to '--maxlen' should be %d or less\n",
275 MAX_RDS_BUFF_LEN);
276 return SG_LIB_SYNTAX_ERROR;
277 }
278 break;
279 case 'M':
280 media = true;
281 break;
282 case 'r':
283 do_raw = true;
284 break;
285 case 'R':
286 o_readonly = true;
287 break;
288 case 't':
289 m_type = true;
290 break;
291 case 'v':
292 verbose_given = true;
293 ++verbose;
294 break;
295 case 'V':
296 version_given = true;
297 break;
298 default:
299 pr2serr("unrecognised option code 0x%x ??\n", c);
300 usage();
301 return SG_LIB_SYNTAX_ERROR;
302 }
303 }
304 if (optind < argc) {
305 if (NULL == device_name) {
306 device_name = argv[optind];
307 ++optind;
308 }
309 if (optind < argc) {
310 for (; optind < argc; ++optind)
311 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
312 usage();
313 return SG_LIB_SYNTAX_ERROR;
314 }
315 }
316 #ifdef DEBUG
317 pr2serr("In DEBUG mode, ");
318 if (verbose_given && version_given) {
319 pr2serr("but override: '-vV' given, zero verbose and continue\n");
320 verbose_given = false;
321 version_given = false;
322 verbose = 0;
323 } else if (! verbose_given) {
324 pr2serr("set '-vv'\n");
325 verbose = 2;
326 } else
327 pr2serr("keep verbose=%d\n", verbose);
328 #else
329 if (verbose_given && version_given)
330 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
331 #endif
332 if (version_given) {
333 pr2serr("version: %s\n", version_str);
334 return 0;
335 }
336
337 if (do_help) {
338 usage();
339 return 0;
340 }
341 if (device_name && inhex_fn) {
342 pr2serr("ignoring DEVICE, best to give DEVICE or --inhex=FN, but "
343 "not both\n");
344 device_name = NULL;
345 }
346 if (0 == maxlen)
347 maxlen = DEF_RDS_BUFF_LEN;
348 rdsBuff = (uint8_t *)sg_memalign(maxlen, 0, &free_rds, verbose > 3);
349 if (NULL == rdsBuff) {
350 pr2serr("unable to sg_memalign %d bytes\n", maxlen);
351 return sg_convert_errno(ENOMEM);
352 }
353 if (NULL == device_name) {
354 if (inhex_fn) {
355 if ((ret = sg_f2hex_arr(inhex_fn, do_raw, false, rdsBuff,
356 &in_len, maxlen))) {
357 if (SG_LIB_LBA_OUT_OF_RANGE == ret) {
358 no_final_msg = true;
359 pr2serr("... decode what we have, --maxlen=%d needs to "
360 "be increased\n", maxlen);
361 } else
362 goto the_end;
363 }
364 if (verbose > 2)
365 pr2serr("Read %d [0x%x] bytes of user supplied data\n",
366 in_len, in_len);
367 if (do_raw)
368 do_raw = false; /* otherwise interferes with decoding */
369 if (in_len < 4) {
370 pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
371 "least)\n", inhex_fn, in_len);
372 ret = SG_LIB_SYNTAX_ERROR;
373 goto the_end;
374 }
375 res = 0;
376 maxlen = in_len;
377 goto start_response;
378 } else {
379 pr2serr("missing device name!\n\n");
380 usage();
381 ret = SG_LIB_FILE_ERROR;
382 no_final_msg = true;
383 goto the_end;
384 }
385 } else
386 in_len = 0;
387
388 if (do_raw) {
389 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
390 perror("sg_set_binary_mode");
391 ret = SG_LIB_FILE_ERROR;
392 no_final_msg = true;
393 goto the_end;
394 }
395 }
396
397 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
398 if (sg_fd < 0) {
399 if (verbose)
400 pr2serr("open error: %s: %s\n", device_name,
401 safe_strerror(-sg_fd));
402 ret = sg_convert_errno(-sg_fd);
403 goto the_end;
404 }
405
406 res = sg_ll_report_density(sg_fd, media, m_type, rdsBuff, maxlen, &resid,
407 true, verbose);
408 start_response:
409 ret = res;
410 if (0 == res) {
411 rlen = maxlen - resid;
412 if (rlen < 4) {
413 pr2serr("Response length (%d) too short\n", rlen);
414 ret = SG_LIB_CAT_MALFORMED;
415 goto the_end;
416 }
417 if (do_raw) {
418 dStrRaw(rdsBuff, rlen);
419 goto the_end;
420 }
421 if (do_hex) {
422 if (2 != do_hex)
423 hex2stdout(rdsBuff, rlen, ((1 == do_hex) ? 1 : -1));
424 else
425 hex2stdout(rdsBuff, rlen, 0);
426 goto the_end;
427 }
428 desc_len = m_type ? RDS_MEDIUM_T_DESC_LEN : RDS_DENSITY_DESC_LEN;
429 ads_len = sg_get_unaligned_be16(rdsBuff + 0) + 2;
430 if (4 == ads_len)
431 goto the_end;
432 if (ads_len < 4) {
433 pr2serr("Badly formatted response, ads_len=%d\n", ads_len - 2);
434 ret = SG_LIB_CAT_MALFORMED;
435 goto the_end;
436 }
437 if (ads_len > rlen) {
438 if (verbose)
439 pr2serr("Trimming response from %d to %d bytes\n", ads_len,
440 rlen);
441 ads_len = rlen;
442 if (4 == ads_len)
443 goto the_end;
444 }
445 num_desc = (ads_len - 4) / desc_len;
446 if (0 != ((ads_len - 4) % desc_len)) {
447 if (verbose)
448 pr2serr("Truncating response to %d descriptors\n", num_desc);
449 }
450 if (m_type)
451 decode_medium_type(rdsBuff + 4, num_desc);
452 else
453 decode_density_code(rdsBuff + 4, num_desc);
454 } else if (SG_LIB_CAT_INVALID_OP == res)
455 pr2serr("%s command not supported\n", rds_s);
456 else {
457 sg_get_category_sense_str(res, sizeof(b), b, verbose);
458 pr2serr("%s command: %s\n", rds_s, b);
459 }
460
461 the_end:
462 if (free_rds)
463 free(free_rds);
464 if (sg_fd >= 0) {
465 res = sg_cmds_close_device(sg_fd);
466 if (res < 0) {
467 pr2serr("close error: %s\n", safe_strerror(-res));
468 if (0 == ret)
469 ret = sg_convert_errno(-res);
470 }
471 }
472 if ((0 == verbose) && (! no_final_msg)) {
473 if (! sg_if_can2stderr("sg_rep_density failed: ", ret))
474 pr2serr("Some error occurred, try again with '-v' "
475 "or '-vv' for more information\n");
476 }
477 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
478 }
479