xref: /aosp_15_r20/external/sg3_utils/src/sg_readcap.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* This code is does a SCSI READ CAPACITY command on the given device
2  * and outputs the result.
3  *
4  * Copyright (C) 1999 - 2020 D. Gilbert
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  *
12  * This program was originally written with Linux 2.4 kernel series.
13  * It now builds for the Linux 2.6, 3 and 4 kernel series and various other
14  * operating systems.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <getopt.h>
26 #define __STDC_FORMAT_MACROS 1
27 #include <inttypes.h>
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include "sg_lib.h"
34 #include "sg_cmds_basic.h"
35 #include "sg_unaligned.h"
36 #include "sg_pr2serr.h"
37 
38 
39 static const char * version_str = "4.05 20200122";
40 
41 #define ME "sg_readcap: "
42 
43 #define RCAP_REPLY_LEN 8
44 #define RCAP16_REPLY_LEN 32
45 
46 static struct option long_options[] = {
47     {"brief", no_argument, 0, 'b'},
48     {"help", no_argument, 0, 'h'},
49     {"hex", no_argument, 0, 'H'},
50     {"lba", required_argument, 0, 'L'},
51     {"long", no_argument, 0, 'l'},
52     {"16", no_argument, 0, 'l'},
53     {"new", no_argument, 0, 'N'},
54     {"old", no_argument, 0, 'O'},
55     {"pmi", no_argument, 0, 'p'},
56     {"raw", no_argument, 0, 'r'},
57     {"readonly", no_argument, 0, 'R'},
58     {"verbose", no_argument, 0, 'v'},
59     {"version", no_argument, 0, 'V'},
60     {"zbc", no_argument, 0, 'z'},
61     {0, 0, 0, 0},
62 };
63 
64 struct opts_t {
65     bool do_brief;
66     bool do_long;
67     bool do_pmi;
68     bool do_raw;
69     bool o_readonly;
70     bool do_zbc;
71     bool opt_new;
72     bool verbose_given;
73     bool version_given;
74     int do_help;
75     int do_hex;
76     int do_lba;
77     int verbose;
78     uint64_t llba;
79     const char * device_name;
80 };
81 
82 
83 static void
usage()84 usage()
85 {
86     pr2serr("Usage: sg_readcap [--16] [--brief] [--help] [--hex] "
87             "[--lba=LBA] [--long]\n"
88             "                  [--pmi] [--raw] [--readonly] [--verbose] "
89             "[--version]\n"
90             "                  [--zbc] DEVICE\n"
91             "  where:\n"
92             "    --16            use READ CAPACITY (16) cdb (same as "
93             "--long)\n"
94             "    --brief|-b      brief, two hex numbers: number of blocks "
95             "and block size\n"
96             "    --help|-h       print this usage message and exit\n"
97             "    --hex|-H        output response in hexadecimal to stdout\n"
98             "    --lba=LBA|-L LBA    yields the last block prior to (head "
99             "movement) delay\n"
100             "                        after LBA [in decimal (def: 0) "
101             "valid with '--pmi']\n"
102             "    --long|-l       use READ CAPACITY (16) cdb (def: use "
103             "10 byte cdb)\n"
104             "    --pmi|-p        partial medium indicator (without this "
105             "option shows\n"
106             "                    total disk capacity) [made obsolete in "
107             "sbc3r26]\n"
108             "    --raw|-r        output response in binary to stdout\n"
109             "    --readonly|-R    open DEVICE read-only (def: RCAP(16) "
110             "read-write)\n"
111             "    --verbose|-v    increase verbosity\n"
112             "    --version|-V    print version string and exit\n"
113             "    --old|-O        use old interface (use as first option)\n"
114             "    --zbc|-z        show rc_basis ZBC field (implies --16)\n\n"
115             "Perform a SCSI READ CAPACITY (10 or 16) command\n");
116 }
117 
118 static void
usage_old()119 usage_old()
120 {
121     pr2serr("Usage:  sg_readcap [-16] [-b] [-h] [-H] [-lba=LBA] "
122             "[-pmi] [-r] [-R]\n"
123             "                   [-v] [-V] [-z] DEVICE\n"
124             "  where:\n"
125             "    -16    use READ CAPACITY (16) cdb (def: use "
126             "10 byte cdb)\n"
127             "    -b     brief, two hex numbers: number of blocks "
128             "and block size\n"
129             "    -h     print this usage message and exit\n"
130             "    -H     output response in hexadecimal to stdout\n"
131             "    -lba=LBA    yields the last block prior to (head "
132             "movement) delay\n"
133             "                after LBA [in hex (def: 0) "
134             "valid with -pmi]\n"
135             "    -pmi   partial medium indicator (without this option "
136             "shows total\n"
137             "           disk capacity)\n"
138             "    -r     output response in binary to stdout\n"
139             "    -R     open DEVICE read-only (def: RCAP(16) read-write)\n"
140             "    -v     increase verbosity\n"
141             "    -V     print version string and exit\n"
142             "    -N|--new   use new interface\n"
143             "    -z     show rc_basis ZBC field (implies -16)\n\n"
144             "Perform a SCSI READ CAPACITY (10 or 16) command\n");
145 }
146 
147 static void
usage_for(const struct opts_t * op)148 usage_for(const struct opts_t * op)
149 {
150     if (op->opt_new)
151         usage();
152     else
153         usage_old();
154 }
155 
156 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])157 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
158 {
159     int c;
160     int a_one = 0;
161     int64_t nn;
162 
163     while (1) {
164         int option_index = 0;
165 
166         c = getopt_long(argc, argv, "16bhHlL:NOprRvVz", long_options,
167                         &option_index);
168         if (c == -1)
169             break;
170 
171         switch (c) {
172         case '1':
173             ++a_one;
174             break;
175         case '6':
176             if (a_one)
177                 op->do_long = true;
178             break;
179         case 'b':
180             op->do_brief = true;
181             break;
182         case 'h':
183         case '?':
184             ++op->do_help;
185             break;
186         case 'H':
187             ++op->do_hex;
188             break;
189         case 'l':
190             op->do_long = true;
191             break;
192         case 'L':
193             nn = sg_get_llnum(optarg);
194             if (-1 == nn) {
195                 pr2serr("bad argument to '--lba='\n");
196                 usage();
197                 return SG_LIB_SYNTAX_ERROR;
198             }
199             op->llba = nn;
200             /* force READ_CAPACITY16 for large lbas */
201             if (op->llba > 0xfffffffeULL)
202                 op->do_long = true;
203             ++op->do_lba;
204             break;
205         case 'N':
206             break;      /* ignore */
207         case 'O':
208             op->opt_new = false;
209             return 0;
210         case 'p':
211             op->do_pmi = true;
212             break;
213         case 'r':
214             op->do_raw = true;
215             break;
216         case 'R':
217             op->o_readonly = true;
218             break;
219         case 'v':
220             op->verbose_given = true;
221             ++op->verbose;
222             break;
223         case 'V':
224             op->version_given = true;
225             break;
226         case 'z':
227             op->do_zbc = true;
228             break;
229         default:
230             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
231             if (op->do_help)
232                 break;
233             usage();
234             return SG_LIB_SYNTAX_ERROR;
235         }
236     }
237     if (optind < argc) {
238         if (NULL == op->device_name) {
239             op->device_name = argv[optind];
240             ++optind;
241         }
242         if (optind < argc) {
243             for (; optind < argc; ++optind)
244                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
245             usage();
246             return SG_LIB_SYNTAX_ERROR;
247         }
248     }
249     return 0;
250 }
251 
252 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])253 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
254 {
255     bool jmp_out;
256     int k, plen, num;
257     const char * cp;
258     uint64_t uu;
259 
260     for (k = 1; k < argc; ++k) {
261         cp = argv[k];
262         plen = strlen(cp);
263         if (plen <= 0)
264             continue;
265         if ('-' == *cp) {
266             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
267                 switch (*cp) {
268                 case '1':
269                     if ('6' == *(cp + 1)) {
270                         op->do_long = true;
271                         ++cp;
272                         --plen;
273                     } else
274                         jmp_out = true;
275                     break;
276                 case 'b':
277                     op->do_brief = true;
278                     break;
279                 case 'h':
280                 case '?':
281                     ++op->do_help;
282                     break;
283                 case 'H':
284                     ++op->do_hex;
285                     break;
286                 case 'N':
287                     op->opt_new = true;
288                     return 0;
289                 case 'O':
290                     break;
291                 case 'p':
292                     if (0 == strncmp("pmi", cp, 3)) {
293                         op->do_pmi = true;
294                         cp += 2;
295                         plen -= 2;
296                     } else
297                         jmp_out = true;
298                     break;
299                 case 'r':
300                     op->do_raw = true;
301                     break;
302                 case 'R':
303                     op->o_readonly = true;
304                     break;
305                 case 'v':
306                     op->verbose_given = true;
307                     ++op->verbose;
308                     break;
309                 case 'V':
310                     op->version_given = true;
311                     break;
312                 case 'z':
313                     op->do_zbc = true;
314                     break;
315                 default:
316                     jmp_out = true;
317                     break;
318                 }
319                 if (jmp_out)
320                     break;
321             }
322             if (plen <= 0)
323                 continue;
324             if (0 == strncmp("lba=", cp, 4)) {
325                 num = sscanf(cp + 4, "%" SCNx64 "", &uu);
326                 if (1 != num) {
327                     printf("Bad value after 'lba=' option\n");
328                     usage();
329                     return SG_LIB_SYNTAX_ERROR;
330                 }
331                 /* force READ_CAPACITY16 for large lbas */
332                 if (uu > 0xfffffffeULL)
333                     op->do_long = true;
334                 op->llba = uu;
335                 ++op->do_lba;
336             } else if (0 == strncmp("-old", cp, 4))
337                 ;
338             else if (jmp_out) {
339                 pr2serr("Unrecognized option: %s\n", cp);
340                 usage();
341                 return SG_LIB_SYNTAX_ERROR;
342             }
343         } else if (0 == op->device_name)
344             op->device_name = cp;
345         else {
346             pr2serr("too many arguments, got: %s, not expecting: %s\n",
347                     op->device_name, cp);
348             usage();
349             return SG_LIB_SYNTAX_ERROR;
350         }
351     }
352     return 0;
353 }
354 
355 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])356 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
357 {
358     int res;
359     char * cp;
360 
361     cp = getenv("SG3_UTILS_OLD_OPTS");
362     if (cp) {
363         op->opt_new = false;
364         res = old_parse_cmd_line(op, argc, argv);
365         if ((0 == res) && op->opt_new)
366             res = new_parse_cmd_line(op, argc, argv);
367     } else {
368         op->opt_new = true;
369         res = new_parse_cmd_line(op, argc, argv);
370         if ((0 == res) && (! op->opt_new))
371             res = old_parse_cmd_line(op, argc, argv);
372     }
373     return res;
374 }
375 
376 static void
dStrRaw(const uint8_t * str,int len)377 dStrRaw(const uint8_t * str, int len)
378 {
379     int k;
380 
381     for (k = 0; k < len; ++k)
382         printf("%c", str[k]);
383 }
384 
385 static const char *
rc_basis_str(int rc_basis,char * b,int blen)386 rc_basis_str(int rc_basis, char * b, int blen)
387 {
388     switch (rc_basis) {
389     case 0:
390         snprintf(b, blen, "last contiguous that's not seq write required");
391         break;
392     case 1:
393         snprintf(b, blen, "last LBA on logical unit");
394         break;
395     default:
396         snprintf(b, blen, "reserved (0x%x)", rc_basis);
397         break;
398     }
399     return b;
400 }
401 
402 
403 int
main(int argc,char * argv[])404 main(int argc, char * argv[])
405 {
406     bool rw_0_flag;
407     int res, prot_en, p_type, lbppbe;
408     int sg_fd = -1;
409     int ret = 0;
410     uint32_t last_blk_addr, block_size;
411     uint64_t llast_blk_addr;
412     uint8_t * resp_buff;
413     uint8_t * free_resp_buff;
414     const int resp_buff_sz = RCAP16_REPLY_LEN;
415     char b[80];
416     struct opts_t opts;
417     struct opts_t * op;
418 
419     op = &opts;
420     memset(op, 0, sizeof(opts));
421     res = parse_cmd_line(op, argc, argv);
422     if (res)
423         return res;
424     if (op->do_help) {
425         usage_for(op);
426         return 0;
427     }
428 #ifdef DEBUG
429     pr2serr("In DEBUG mode, ");
430     if (op->verbose_given && op->version_given) {
431         pr2serr("but override: '-vV' given, zero verbose and continue\n");
432         op->verbose_given = false;
433         op->version_given = false;
434         op->verbose = 0;
435     } else if (! op->verbose_given) {
436         pr2serr("set '-vv'\n");
437         op->verbose = 2;
438     } else
439         pr2serr("keep verbose=%d\n", op->verbose);
440 #else
441     if (op->verbose_given && op->version_given)
442         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
443 #endif
444     if (op->version_given) {
445         pr2serr("Version string: %s\n", version_str);
446         return 0;
447     }
448 
449     if (NULL == op->device_name) {
450         pr2serr("No DEVICE argument given\n\n");
451         usage_for(op);
452         return SG_LIB_SYNTAX_ERROR;
453     }
454     if (op->do_raw) {
455         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
456             perror("sg_set_binary_mode");
457             return SG_LIB_FILE_ERROR;
458         }
459     }
460     if (op->do_zbc) {
461         if (! op->do_long)
462             op->do_long = true;
463     }
464 
465     resp_buff = sg_memalign(resp_buff_sz, 0, &free_resp_buff, false);
466     if (NULL == resp_buff) {
467         pr2serr("Unable to allocate %d bytes on heap\n", resp_buff_sz);
468         return sg_convert_errno(ENOMEM);
469     }
470     if ((! op->do_pmi) && (op->llba > 0)) {
471         pr2serr(ME "lba can only be non-zero when '--pmi' is set\n");
472         usage_for(op);
473         ret = SG_LIB_CONTRADICT;
474         goto fini;
475     }
476     if (op->do_long)
477         rw_0_flag = op->o_readonly;
478     else
479         rw_0_flag = true;  /* RCAP(10) has opened RO in past, so leave */
480     if ((sg_fd = sg_cmds_open_device(op->device_name, rw_0_flag,
481                                      op->verbose)) < 0) {
482         pr2serr(ME "error opening file: %s: %s\n", op->device_name,
483                 safe_strerror(-sg_fd));
484         ret = sg_convert_errno(-sg_fd);
485         goto fini;
486     }
487 
488     if (! op->do_long) {
489         res = sg_ll_readcap_10(sg_fd, op->do_pmi, (unsigned int)op->llba,
490                                resp_buff, RCAP_REPLY_LEN, true,
491                                op->verbose);
492         ret = res;
493         if (0 == res) {
494             if (op->do_hex || op->do_raw) {
495                 if (op->do_raw)
496                     dStrRaw(resp_buff, RCAP_REPLY_LEN);
497                 else if (op->do_hex > 2)
498                     hex2stdout(resp_buff, RCAP_REPLY_LEN, -1);
499                 else
500                     hex2stdout(resp_buff, RCAP_REPLY_LEN, 1);
501                 goto fini;
502             }
503             last_blk_addr = sg_get_unaligned_be32(resp_buff + 0);
504             if (0xffffffff != last_blk_addr) {
505                 block_size = sg_get_unaligned_be32(resp_buff + 4);
506                 if (op->do_brief) {
507                     printf("0x%" PRIx32 " 0x%" PRIx32 "\n",
508                            last_blk_addr + 1, block_size);
509                     goto fini;
510                 }
511                 printf("Read Capacity results:\n");
512                 if (op->do_pmi)
513                     printf("   PMI mode: given lba=0x%" PRIx64 ", last lba "
514                            "before delay=0x%" PRIx32 "\n", op->llba,
515                            last_blk_addr);
516                 else
517                     printf("   Last LBA=%" PRIu32 " (0x%" PRIx32 "), Number "
518                            "of logical blocks=%" PRIu32 "\n", last_blk_addr,
519                             last_blk_addr, last_blk_addr + 1);
520                 printf("   Logical block length=%u bytes\n", block_size);
521                 if (! op->do_pmi) {
522                     uint64_t total_sz = last_blk_addr + 1;
523                     double sz_mb, sz_gb;
524 
525                     total_sz *= block_size;
526                     sz_mb = ((double)(last_blk_addr + 1) * block_size) /
527                             (double)(1048576);
528                     sz_gb = ((double)(last_blk_addr + 1) * block_size) /
529                             (double)(1000000000L);
530                     printf("Hence:\n");
531 #ifdef SG_LIB_MINGW
532                     printf("   Device size: %" PRIu64 " bytes, %g MiB, %g "
533                            "GB", total_sz, sz_mb, sz_gb);
534 #else
535                     printf("   Device size: %" PRIu64 " bytes, %.1f MiB, "
536                            "%.2f GB", total_sz, sz_mb, sz_gb);
537 #endif
538                     if (sz_gb > 2000) {
539 #ifdef SG_LIB_MINGW
540                         printf(", %g TB", sz_gb / 1000);
541 #else
542                         printf(", %.2f TB", sz_gb / 1000);
543 #endif
544                     }
545                     printf("\n");
546                 }
547                 goto fini;
548             } else {
549                 printf("READ CAPACITY (10) indicates device capacity too "
550                        "large\n  now trying 16 byte cdb variant\n");
551                 op->do_long = true;
552             }
553         } else if (SG_LIB_CAT_INVALID_OP == res) {
554             op->do_long = true;
555             sg_cmds_close_device(sg_fd);
556             if ((sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly,
557                                              op->verbose)) < 0) {
558                 pr2serr(ME "error re-opening file: %s (rw): %s\n",
559                         op->device_name, safe_strerror(-sg_fd));
560                 ret = sg_convert_errno(-sg_fd);
561                 goto fini;
562             }
563             if (op->verbose)
564                 pr2serr("READ CAPACITY (10) not supported, trying READ "
565                         "CAPACITY (16)\n");
566         } else if (res) {
567             sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
568             pr2serr("READ CAPACITY (10) failed: %s\n", b);
569         }
570     }
571     if (op->do_long) {
572         res = sg_ll_readcap_16(sg_fd, op->do_pmi, op->llba, resp_buff,
573                                RCAP16_REPLY_LEN, true, op->verbose);
574         ret = res;
575         if (0 == res) {
576             if (op->do_hex || op->do_raw) {
577                 if (op->do_raw)
578                     dStrRaw(resp_buff, RCAP16_REPLY_LEN);
579                 else if (op->do_hex > 2)
580                     hex2stdout(resp_buff, RCAP16_REPLY_LEN, -1);
581                 else
582                     hex2stdout(resp_buff, RCAP16_REPLY_LEN, 1);
583                 goto fini;
584             }
585             llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0);
586             block_size = sg_get_unaligned_be32(resp_buff + 8);
587             if (op->do_brief) {
588                 printf("0x%" PRIx64 " 0x%" PRIx32 "\n", llast_blk_addr + 1,
589                        block_size);
590                 goto fini;
591             }
592             prot_en = !!(resp_buff[12] & 0x1);
593             p_type = ((resp_buff[12] >> 1) & 0x7);
594             printf("Read Capacity results:\n");
595             printf("   Protection: prot_en=%d, p_type=%d, p_i_exponent=%d",
596                    prot_en, p_type, ((resp_buff[13] >> 4) & 0xf));
597             if (prot_en)
598                 printf(" [type %d protection]\n", p_type + 1);
599             else
600                 printf("\n");
601             if (op->do_zbc) {
602                 int rc_basis = (resp_buff[12] >> 4) & 0x3;
603 
604                 printf("   ZBC's rc_basis=%d [%s]\n", rc_basis,
605                        rc_basis_str(rc_basis, b, sizeof(b)));
606             }
607             printf("   Logical block provisioning: lbpme=%d, lbprz=%d\n",
608                    !!(resp_buff[14] & 0x80), !!(resp_buff[14] & 0x40));
609             if (op->do_pmi)
610                 printf("   PMI mode: given lba=0x%" PRIx64 ", last lba "
611                        "before delay=0x%" PRIx64 "\n", op->llba,
612                        llast_blk_addr);
613             else
614                 printf("   Last LBA=%" PRIu64 " (0x%" PRIx64 "), Number of "
615                        "logical blocks=%" PRIu64 "\n", llast_blk_addr,
616                        llast_blk_addr, llast_blk_addr + 1);
617             printf("   Logical block length=%" PRIu32 " bytes\n", block_size);
618             lbppbe = resp_buff[13] & 0xf;
619             printf("   Logical blocks per physical block exponent=%d",
620                    lbppbe);
621             if (lbppbe > 0)
622                 printf(" [so physical block length=%u bytes]\n",
623                        block_size * (1 << lbppbe));
624             else
625                 printf("\n");
626             printf("   Lowest aligned LBA=%d\n",
627                    ((resp_buff[14] & 0x3f) << 8) + resp_buff[15]);
628             if (! op->do_pmi) {
629                 uint64_t total_sz = llast_blk_addr + 1;
630                 double sz_mb, sz_gb;
631 
632                 total_sz *= block_size;
633                 sz_mb = ((double)(llast_blk_addr + 1) * block_size) /
634                         (double)(1048576);
635                 sz_gb = ((double)(llast_blk_addr + 1) * block_size) /
636                         (double)(1000000000L);
637                 printf("Hence:\n");
638 #ifdef SG_LIB_MINGW
639                 printf("   Device size: %" PRIu64 " bytes, %g MiB, %g GB",
640                        total_sz, sz_mb, sz_gb);
641 #else
642                 printf("   Device size: %" PRIu64 " bytes, %.1f MiB, %.2f "
643                        "GB", total_sz, sz_mb, sz_gb);
644 #endif
645                 if (sz_gb > 2000) {
646 #ifdef SG_LIB_MINGW
647                     printf(", %g TB", sz_gb / 1000);
648 #else
649                     printf(", %.2f TB", sz_gb / 1000);
650 #endif
651                 }
652                 printf("\n");
653             }
654             goto fini;
655         } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
656             pr2serr("bad field in READ CAPACITY (16) cdb including "
657                     "unsupported service action\n");
658         else if (res) {
659             sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
660             pr2serr("READ CAPACITY (16) failed: %s\n", b);
661         }
662     }
663     if (op->do_brief)
664         printf("0x0 0x0\n");
665 fini:
666     if (free_resp_buff)
667         free(free_resp_buff);
668     if (sg_fd >= 0) {
669         res = sg_cmds_close_device(sg_fd);
670         if (res < 0) {
671             pr2serr("close error: %s\n", safe_strerror(-res));
672             if (0 == ret)
673                 ret = sg_convert_errno(-res);
674         }
675     }
676     if (0 == op->verbose) {
677         if (! sg_if_can2stderr("sg_readcap failed: ", ret))
678             pr2serr("Some error occurred, try again with '-v' "
679                     "or '-vv' for more information\n");
680     }
681     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
682 }
683