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