/
githubmirror
/
sg3_utils
Обзор
Документация
Войти
/
githubmirror
/
sg3_utils
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/sg_readcap.c
941 строка
32 KB
Douglas Gilbert
major addition of const, mainly to file scope statics
24 июн 2023, 05:05
24 июн 2023, 05:05
0c29469
Код
Авторство
О чём код?
/* This code is does a SCSI READ CAPACITY command on the given device * and outputs the result. * * Copyright (C) 1999 - 2023 D. Gilbert * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * This program was originally written with Linux 2.4 kernel series. * It now builds for the Linux 2.6, 3 and 4 kernel series and various other * operating systems. */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdbool.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <getopt.h> #define __STDC_FORMAT_MACROS 1 #include <inttypes.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_unaligned.h" #include "sg_pr2serr.h" #include "sg_json_sg_lib.h" static const char * version_str = "4.13 20230519"; static const char * my_name = "sg_readcap: "; #define RCAP_REPLY_LEN 8 #define RCAP16_REPLY_LEN 32 static const struct option long_options[] = { {"brief", no_argument, 0, 'b'}, {"help", no_argument, 0, 'h'}, {"hex", no_argument, 0, 'H'}, {"inhex", required_argument, 0, 'i'}, {"json", optional_argument, 0, '^'}, /* short option is '-j' */ {"js-file", required_argument, 0, 'J'}, {"js_file", required_argument, 0, 'J'}, {"lba", required_argument, 0, 'L'}, {"long", no_argument, 0, 'l'}, {"16", no_argument, 0, 'l'}, {"new", no_argument, 0, 'N'}, {"old", no_argument, 0, 'O'}, {"pmi", no_argument, 0, 'p'}, {"raw", no_argument, 0, 'r'}, {"readonly", no_argument, 0, 'R'}, {"10", no_argument, 0, 'T'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"zbc", no_argument, 0, 'z'}, {0, 0, 0, 0}, }; struct opts_t { bool do_brief; bool do_json; bool do_long; bool do_pmi; bool do_raw; bool o_readonly; bool do_zbc; bool opt_new; bool verbose_given; bool version_given; int do_help; int do_hex; int do_lba; int verbose; uint64_t llba; const char * device_name; const char * inhex_fn; const char * json_arg; const char * js_file; sgj_state json_st; }; static const char * rc10_pd_sn = "read_capacity_10_parameter_data"; static const char * rc16_pd_sn = "read_capacity_16_parameter_data"; static const char * rlba_sn = "returned_logical_block_address"; static const char * lblib_sn = "logical_block_length_in_bytes"; static const char * lbppbe_s = "Logical blocks per physical block exponent"; static void usage() { pr2serr("Usage: sg_readcap [--10] [--16] [--brief] [--help] [--hex] " "[--inhex-FN]\n" " [--json[=JO]] [--js-file=JFN] [--lba=LBA] " "[--long] [--pmi]\n" " [--raw] [--readonly] [--verbose] [--version] " "[--zbc]\n" " DEVICE\n" " where:\n" " --10 use READ CAPACITY (10) cdb (this is the " "default)\n" " --16 use READ CAPACITY (16) cdb (same as " "--long)\n" " --brief|-b brief, two hex numbers: number of blocks " "and block size\n" " --help|-h print this usage message and exit\n" " --hex|-H output response in hexadecimal to stdout\n" " --inhex=FN|-i FN contents of file FN treated as hex " "and used\n" " instead of DEVICE which is ignored\n" " --json[=JO]|-j[=JO] output in JSON instead of plain " "text\n" " Use --json=? for JSON help\n" " --js-file=JFN|-J JFN JFN is a filename to which JSON " "output is\n" " written (def: stdout); truncates " "then writes\n" " --lba=LBA|-L LBA yields the last block prior to (head " "movement) delay\n" " after LBA [in decimal (def: 0) " "valid with '--pmi']\n" " --long|-l use READ CAPACITY (16) cdb (def: use " "10 byte cdb)\n" " --old|-O use old interface (use as first option)\n" " --pmi|-p partial medium indicator (without this " "option shows\n" " total disk capacity) [made obsolete in " "sbc3r26]\n" " --raw|-r output response in binary to stdout\n" " --readonly|-R open DEVICE read-only (def: RCAP(16) " "read-write)\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n" " --zbc|-z show rc_basis ZBC field (implies --16)\n\n" "Perform a SCSI READ CAPACITY (10 or 16) command\n"); } static void usage_old() { pr2serr("Usage: sg_readcap [-16] [-b] [-h] [-H] [-lba=LBA] " "[-pmi] [-r] [-R]\n" " [-v] [-V] [-z] DEVICE\n" " where:\n" " -16 use READ CAPACITY (16) cdb (def: use " "10 byte cdb)\n" " -b brief, two hex numbers: number of blocks " "and block size\n" " -h print this usage message and exit\n" " -H output response in hexadecimal to stdout\n" " -lba=LBA yields the last block prior to (head " "movement) delay\n" " after LBA [in hex (def: 0) " "valid with -pmi]\n" " -pmi partial medium indicator (without this option " "shows total\n" " disk capacity)\n" " -r output response in binary to stdout\n" " -R open DEVICE read-only (def: RCAP(16) read-write)\n" " -v increase verbosity\n" " -V print version string and exit\n" " -N|--new use new interface\n" " -z show rc_basis ZBC field (implies -16)\n\n" "Perform a SCSI READ CAPACITY (10 or 16) command\n"); } static void usage_for(const struct opts_t * op) { if (op->opt_new) usage(); else usage_old(); } /* Handles short options after '-j' including a sequence of short options * that include one 'j' (for JSON). Want optional argument to '-j' to be * prefixed by '='. Return 0 for good, SG_LIB_SYNTAX_ERROR for syntax error * and SG_LIB_OK_FALSE for exit with no error. */ static int chk_short_opts(const char sopt_ch, struct opts_t * op) { /* only need to process short, non-argument options */ int a_one = 0; switch (sopt_ch) { case '1': ++a_one; break; case '6': if (a_one) op->do_long = true; break; case 'b': op->do_brief = true; break; case 'h': case '?': ++op->do_help; break; case 'H': ++op->do_hex; break; case 'j': break; /* simply ignore second 'j' (e.g. '-jxj') */ case 'l': op->do_long = true; break; case 'N': break; /* ignore */ case 'O': op->opt_new = false; return 0; case 'p': op->do_pmi = true; break; case 'r': op->do_raw = true; break; case 'R': op->o_readonly = true; break; case 'T': op->do_long = false; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; case 'z': op->do_zbc = true; break; default: pr2serr("unrecognised option code %c [0x%x] ??\n", sopt_ch, sopt_ch); return SG_LIB_SYNTAX_ERROR; } return 0; } static int new_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { int c; int a_one = 0; int64_t nn; while (1) { int option_index = 0; c = getopt_long(argc, argv, "^16bhHi:j::J:lL:NOprRTvVz", long_options, &option_index); if (c == -1) break; switch (c) { case '1': ++a_one; break; case '6': if (a_one) op->do_long = true; break; case 'b': op->do_brief = true; break; case 'h': case '?': ++op->do_help; break; case 'H': ++op->do_hex; break; case 'i': op->inhex_fn = optarg; break; case 'j': /* for: -j[=JO] */ case '^': /* for: --json[=JO] */ op->do_json = true; /* Now want '=' to precede all JSON optional arguments */ if (optarg) { int k, n, q; if ('^' == c) { op->json_arg = optarg; break; } else if ('=' == *optarg) { op->json_arg = optarg + 1; break; } n = strlen(optarg); for (k = 0; k < n; ++k) { q = chk_short_opts(*(optarg + k), op); if (SG_LIB_SYNTAX_ERROR == q) return SG_LIB_SYNTAX_ERROR; if (SG_LIB_OK_FALSE == q) return 0; } } else op->json_arg = NULL; break; case 'J': op->do_json = true; op->js_file = optarg; break; case 'l': op->do_long = true; break; case 'L': nn = sg_get_llnum(optarg); if (-1 == nn) { pr2serr("bad argument to '--lba='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } op->llba = nn; /* force READ_CAPACITY16 for large lbas */ if (op->llba > 0xfffffffeULL) op->do_long = true; ++op->do_lba; break; case 'N': break; /* ignore */ case 'O': op->opt_new = false; return 0; case 'p': op->do_pmi = true; break; case 'r': op->do_raw = true; break; case 'R': op->o_readonly = true; break; case 'T': op->do_long = false; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; case 'z': op->do_zbc = true; break; default: pr2serr("unrecognised option code %c [0x%x]\n", c, c); if (op->do_help) break; usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if (NULL == op->device_name) { op->device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } return 0; } static int old_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { bool jmp_out; int k, plen, num; const char * cp; uint64_t uu; for (k = 1; k < argc; ++k) { cp = argv[k]; plen = strlen(cp); if (plen <= 0) continue; if ('-' == *cp) { for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) { switch (*cp) { case '1': if ('6' == *(cp + 1)) { op->do_long = true; ++cp; --plen; } else jmp_out = true; break; case 'b': op->do_brief = true; break; case 'h': case '?': ++op->do_help; break; case 'H': ++op->do_hex; break; case 'j': op->do_json = true; /* ignore optional argument if given */ break; case 'N': op->opt_new = true; return 0; case 'O': break; case 'p': if (0 == strncmp("pmi", cp, 3)) { op->do_pmi = true; cp += 2; plen -= 2; } else jmp_out = true; break; case 'r': op->do_raw = true; break; case 'R': op->o_readonly = true; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; case 'z': op->do_zbc = true; break; default: jmp_out = true; break; } if (jmp_out) break; } if (plen <= 0) continue; if (0 == strncmp("lba=", cp, 4)) { num = sscanf(cp + 4, "%" SCNx64 "", &uu); if (1 != num) { pr2serr("Bad value after 'lba=' option\n"); usage(); return SG_LIB_SYNTAX_ERROR; } /* force READ_CAPACITY16 for large lbas */ if (uu > 0xfffffffeULL) op->do_long = true; op->llba = uu; ++op->do_lba; } else if (0 == strncmp("-old", cp, 4)) ; else if (jmp_out) { pr2serr("Unrecognized option: %s\n", cp); usage(); return SG_LIB_SYNTAX_ERROR; } } else if (0 == op->device_name) op->device_name = cp; else { pr2serr("too many arguments, got: %s, not expecting: %s\n", op->device_name, cp); usage(); return SG_LIB_SYNTAX_ERROR; } } return 0; } static int parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { int res; char * cp; cp = getenv("SG3_UTILS_OLD_OPTS"); if (cp) { op->opt_new = false; res = old_parse_cmd_line(op, argc, argv); if ((0 == res) && op->opt_new) res = new_parse_cmd_line(op, argc, argv); } else { op->opt_new = true; res = new_parse_cmd_line(op, argc, argv); if ((0 == res) && (! op->opt_new)) res = old_parse_cmd_line(op, argc, argv); } return res; } static void dStrRaw(const uint8_t * str, int len) { int k; for (k = 0; k < len; ++k) printf("%c", str[k]); } static const char * rc_basis_str(int rc_basis, char * b, int blen) { switch (rc_basis) { case 0: snprintf(b, blen, "last contiguous that's not seq write required"); break; case 1: snprintf(b, blen, "last LBA on logical unit"); break; default: snprintf(b, blen, "reserved (0x%x)", rc_basis); break; } return b; } int main(int argc, char * argv[]) { bool rw_0_flag, as_json, lbpme, lbprz; int n, res, prot_en, p_type, lbppbe, in_len, rc_basis, p_i_exponent; int lalba; int sg_fd = -1; int ret = 0; uint32_t last_blk_addr, block_size; const uint32_t pg_sz = sg_get_page_size(); uint64_t llast_blk_addr; uint8_t * resp_buff; uint8_t * free_resp_buff = NULL; sgj_state * jsp; sgj_opaque_p jop = NULL; sgj_opaque_p jo2p = NULL; const int resp_buff_sz = RCAP16_REPLY_LEN; char b[256]; char d[80]; struct opts_t opts; struct opts_t * op; static const int blen = sizeof(b); static const int dlen = sizeof(d); op = &opts; memset(op, 0, sizeof(opts)); if (getenv("SG3_UTILS_INVOCATION")) sg_rep_invocation(my_name, version_str, argc, argv, stderr); res = parse_cmd_line(op, argc, argv); if (res) return res; if (op->do_help) { usage_for(op); return 0; } #ifdef DEBUG pr2serr("In DEBUG mode, "); if (op->verbose_given && op->version_given) { pr2serr("but override: '-vV' given, zero verbose and continue\n"); op->verbose_given = false; op->version_given = false; op->verbose = 0; } else if (! op->verbose_given) { pr2serr("set '-vv'\n"); op->verbose = 2; } else pr2serr("keep verbose=%d\n", op->verbose); #else if (op->verbose_given && op->version_given) pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); #endif if (op->version_given) { pr2serr("Version string: %s\n", version_str); return 0; } jsp = &op->json_st; if (op->do_json) { if (! sgj_init_state(jsp, op->json_arg)) { int bad_char = jsp->first_bad_char; char e[1500]; if (bad_char) { pr2serr("bad argument to --json= option, unrecognized " "character '%c'\n\n", bad_char); } sg_json_usage(0, e, sizeof(e)); pr2serr("%s", e); return SG_LIB_SYNTAX_ERROR; } jop = sgj_start_r(my_name, version_str, argc, argv, jsp); } as_json = jsp->pr_as_json; if (op->inhex_fn) { if (op->device_name) { if (! as_json) pr2serr("ignoring DEVICE, best to give DEVICE or " "--inhex=FN, but not both\n"); op->device_name = NULL; } } else if (NULL == op->device_name) { pr2serr("No DEVICE argument given\n\n"); usage_for(op); return SG_LIB_SYNTAX_ERROR; } if (op->do_raw) { if (sg_set_binary_mode(STDOUT_FILENO) < 0) { perror("sg_set_binary_mode"); return SG_LIB_FILE_ERROR; } } if (op->do_zbc) { if (! op->do_long) op->do_long = true; } if ((! op->do_pmi) && (op->llba > 0)) { pr2serr("%slba can only be non-zero when '--pmi' is set\n", my_name); usage_for(op); ret = SG_LIB_CONTRADICT; goto fini; } resp_buff = sg_memalign(resp_buff_sz, 0, &free_resp_buff, false); if (NULL == resp_buff) { pr2serr("Unable to allocate %d bytes on heap\n", resp_buff_sz); return sg_convert_errno(ENOMEM); } if (op->inhex_fn) { if ((res = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, resp_buff, &in_len, pg_sz))) { if (SG_LIB_LBA_OUT_OF_RANGE == res) pr2serr("decode buffer [%d] not large enough??\n", pg_sz); ret = res; goto fini; } if (op->verbose > 2) pr2serr("Read %d [0x%x] bytes of user supplied data\n", in_len, in_len); if (op->do_raw) op->do_raw = false; /* can interfere on decode */ if (in_len < 4) { pr2serr("--inhex=%s only decoded %d bytes (needs 4 at " "least)\n", op->inhex_fn, in_len); ret = SG_LIB_SYNTAX_ERROR; goto fini; } } else { if (op->do_long) rw_0_flag = op->o_readonly; else rw_0_flag = true; /* RCAP(10) has opened RO in past, so leave */ if ((sg_fd = sg_cmds_open_device(op->device_name, rw_0_flag, op->verbose)) < 0) { pr2serr("%serror opening file: %s: %s\n", my_name, op->device_name, safe_strerror(-sg_fd)); ret = sg_convert_errno(-sg_fd); goto fini; } } if (! op->do_long) { if (sg_fd >= 0) res = sg_ll_readcap_10(sg_fd, op->do_pmi, (unsigned int)op->llba, resp_buff, RCAP_REPLY_LEN, true, op->verbose); else res = 0; ret = res; if (0 == res) { if (op->do_hex || op->do_raw) { if (op->do_raw) dStrRaw(resp_buff, RCAP_REPLY_LEN); else if (op->do_hex > 2) { if (op->do_hex > 3) printf("\n# %s\n", rc10_pd_sn); hex2stdout(resp_buff, RCAP_REPLY_LEN, -1); } else hex2stdout(resp_buff, RCAP_REPLY_LEN, (int)(1 == op->do_hex)); goto fini; } jo2p = sgj_named_subobject_r(jsp, jop, rc10_pd_sn); last_blk_addr = sg_get_unaligned_be32(resp_buff + 0); sgj_js_nv_ihex_nex(jsp, jo2p, rlba_sn, last_blk_addr, true, "size is 1 plus this value [unit: logical block]"); block_size = sg_get_unaligned_be32(resp_buff + 4); sgj_js_nv_i(jsp, jo2p, lblib_sn, block_size); if (0xffffffff != last_blk_addr) { if (op->do_brief) { sgj_pr_hr(jsp, "0x%" PRIx32 " 0x%" PRIx32 "\n", last_blk_addr + 1, block_size); goto fini; } sgj_pr_hr(jsp, "Read Capacity results:\n"); if (op->do_pmi) sgj_pr_hr(jsp, " PMI mode: given lba=0x%" PRIx64 ", " "last lba before delay=0x%" PRIx32 "\n", op->llba, last_blk_addr); else sgj_pr_hr(jsp, " Last LBA=%" PRIu32 " (0x%" PRIx32 "), " "Number of logical blocks=%" PRIu32 "\n", last_blk_addr, last_blk_addr, last_blk_addr + 1); sgj_pr_hr(jsp, " Logical block length=%u bytes\n", block_size); if (! op->do_pmi) { uint64_t total_sz = last_blk_addr + 1; double sz_mb, sz_gb; total_sz *= block_size; sz_mb = ((double)(last_blk_addr + 1) * block_size) / (double)(1048576); sz_gb = ((double)(last_blk_addr + 1) * block_size) / (double)(1000000000L); sgj_pr_hr(jsp, "Hence:\n"); #ifdef SG_LIB_MINGW n = sg_scnpr(b, blen, " Device size: %" PRIu64 " bytes, %g MiB, %g GB", total_sz, sz_mb, sz_gb); #else n = sg_scnpr(b, blen, " Device size: %" PRIu64 " bytes, %.1f MiB, %.2f GB", total_sz, sz_mb, sz_gb); #endif if (sz_gb > 2000) { #ifdef SG_LIB_MINGW sg_scn3pr(b, blen, n, ", %g TB", sz_gb / 1000); #else sg_scn3pr(b, blen, n, ", %.2f TB", sz_gb / 1000); #endif } sgj_pr_hr(jsp, "%s\n", b); } goto fini; } else { sgj_pr_hr(jsp, "READ CAPACITY (10) indicates device capacity " "too large\n now trying 16 byte cdb variant\n"); op->do_long = true; } } else if ((SG_LIB_CAT_INVALID_OP == res) && (sg_fd >= 0)) { op->do_long = true; sg_cmds_close_device(sg_fd); if ((sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly, op->verbose)) < 0) { pr2serr("%serror re-opening file: %s (rw): %s\n", my_name, op->device_name, safe_strerror(-sg_fd)); ret = sg_convert_errno(-sg_fd); goto fini; } if (op->verbose) pr2serr("READ CAPACITY (10) not supported, trying READ " "CAPACITY (16)\n"); } else if (res) { sg_get_category_sense_str(res, blen, b, op->verbose); pr2serr("READ CAPACITY (10) failed: %s\n", b); } } if (op->do_long) { if (sg_fd >= 0) res = sg_ll_readcap_16(sg_fd, op->do_pmi, op->llba, resp_buff, RCAP16_REPLY_LEN, true, op->verbose); else res = 0; ret = res; if (0 == res) { if (op->do_hex || op->do_raw) { if (op->do_raw) dStrRaw(resp_buff, RCAP16_REPLY_LEN); else if (op->do_hex > 2) { if (op->do_hex > 3) printf("\n# %s\n", rc16_pd_sn); hex2stdout(resp_buff, RCAP16_REPLY_LEN, -1); } else hex2stdout(resp_buff, RCAP16_REPLY_LEN, (int)(1 == op->do_hex)); goto fini; } jo2p = sgj_named_subobject_r(jsp, jop, rc16_pd_sn); llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0); sgj_js_nv_ihex_nex(jsp, jo2p, rlba_sn, llast_blk_addr, true, "size is 1 plus this value [unit: logical block]"); block_size = sg_get_unaligned_be32(resp_buff + 8); sgj_js_nv_i(jsp, jo2p, lblib_sn, block_size); if (op->do_brief) { sgj_pr_hr(jsp, "0x%" PRIx64 " 0x%" PRIx32 "\n", llast_blk_addr + 1, block_size); goto fini; } rc_basis = (resp_buff[12] >> 4) & 0x3; sgj_js_nv_ihex_nex(jsp, jo2p, "rc_basis", rc_basis, false, "ZBC-2"); prot_en = !!(resp_buff[12] & 0x1); p_type = ((resp_buff[12] >> 1) & 0x7); sgj_js_nv_ihex_nex(jsp, jo2p, "p_type", p_type, false, "Protection TYPE"); sgj_js_nv_ihex_nex(jsp, jo2p, "prot_en", prot_en, false, "PROTection ENabled"); p_i_exponent = (resp_buff[13] >> 4) & 0xf; sgj_pr_hr(jsp, "Read Capacity results:\n"); sg_scnpr(b, blen, " Protection: prot_en=%d, p_type=%d, " "p_i_exponent=%d", prot_en, p_type, p_i_exponent); if (prot_en) sgj_pr_hr(jsp, "%s [type %d protection]\n", b, p_type + 1); else sgj_pr_hr(jsp, "%s\n", b); sgj_js_nv_ihex_nex(jsp, jo2p, "p_i_exponent", p_i_exponent, true, "Protection (information) Interval " "EXPONENT"); if (op->do_zbc) { sgj_pr_hr(jsp, " ZBC's rc_basis=%d [%s]\n", rc_basis, rc_basis_str(rc_basis, b, blen)); } lbppbe = resp_buff[13] & 0xf; sgj_js_nv_ihex(jsp, jo2p, sgj_convert2snake(lbppbe_s, d, dlen), lbppbe); lbpme = !!(resp_buff[14] & 0x80); lbprz = !!(resp_buff[14] & 0x40); sgj_pr_hr(jsp, " Logical block provisioning: lbpme=%d, " "lbprz=%d\n", lbpme, lbprz); sgj_js_nv_ihex_nex(jsp, jo2p, "lbpme", lbpme, false, "Logical " "Block Provisioning Management Enabled"); sgj_js_nv_ihex_nex(jsp, jo2p, "lbprz", lbprz, false, "Logical " "Block Provisioning Read Zeros"); lalba = ((resp_buff[14] & 0x3f) << 8) + resp_buff[15]; sgj_js_nv_ihex(jsp, jo2p, "lowest_aligned_logical_block_address", lalba); if (op->do_pmi) sg_scnpr(b, blen, " PMI mode: given lba=0x%" PRIx64 ", last lba before delay=0x%" PRIx64 "\n", op->llba, llast_blk_addr); else sg_scnpr(b, blen, " Last LBA=%" PRIu64 " (0x%" PRIx64 "), " "Number of logical blocks=%" PRIu64 "\n", llast_blk_addr, llast_blk_addr, llast_blk_addr + 1); sgj_pr_hr(jsp, "%s Logical block length=%" PRIu32 " bytes\n", b, block_size); sg_scnpr(b, blen, " %s=%d", lbppbe_s, lbppbe); if (lbppbe > 0) sgj_pr_hr(jsp, "%s [so physical block length=%u bytes]\n", b, block_size * (1 << lbppbe)); else sgj_pr_hr(jsp, "%s\n", b); sgj_pr_hr(jsp, " Lowest aligned LBA=%d\n", ((resp_buff[14] & 0x3f) << 8) + resp_buff[15]); if (! op->do_pmi) { uint64_t total_sz = llast_blk_addr + 1; double sz_mb, sz_gb; total_sz *= block_size; sz_mb = ((double)(llast_blk_addr + 1) * block_size) / (double)(1048576); sz_gb = ((double)(llast_blk_addr + 1) * block_size) / (double)(1000000000L); sgj_pr_hr(jsp, "Hence:\n"); #ifdef SG_LIB_MINGW n = sg_scnpr(b, blen, " Device size: %" PRIu64 " bytes, " "%g MiB, %g GB", total_sz, sz_mb, sz_gb); #else n = sg_scnpr(b, blen, " Device size: %" PRIu64 " bytes, " "%.1f MiB, %.2f GB", total_sz, sz_mb, sz_gb); #endif if (sz_gb > 2000) { #ifdef SG_LIB_MINGW sg_scn3pr(b, blen, n,", %g TB", sz_gb / 1000); #else sg_scn3pr(b, blen, n, ", %.2f TB", sz_gb / 1000); #endif } sgj_pr_hr(jsp, "%s\n", b); } goto fini; } else if (SG_LIB_CAT_ILLEGAL_REQ == res) pr2serr("bad field in READ CAPACITY (16) cdb including " "unsupported service action\n"); else if (res) { sg_get_category_sense_str(res, blen, b, op->verbose); pr2serr("READ CAPACITY (16) failed: %s\n", b); } } if (op->do_brief) sgj_pr_hr(jsp, "0x0 0x0\n"); fini: if (free_resp_buff) free(free_resp_buff); if (sg_fd >= 0) { res = sg_cmds_close_device(sg_fd); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == ret) ret = sg_convert_errno(-res); } } if (0 == op->verbose) { if (! sg_if_can2stderr("sg_readcap failed: ", ret)) pr2serr("Some error occurred, try again with '-v' " "or '-vv' for more information\n"); } ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER; if (op->do_json) { FILE * fp = stdout; if (op->js_file) { if ((1 != strlen(op->js_file)) || ('-' != op->js_file[0])) { fp = fopen(op->js_file, "w"); /* truncate if exists */ if (NULL == fp) { pr2serr("unable to open file: %s\n", op->js_file); res = SG_LIB_FILE_ERROR; } } /* '--js-file=-' will send JSON output to stdout */ } if (fp) sgj_js2file(jsp, NULL, res, fp); if (op->js_file && fp && (stdout != fp)) fclose(fp); sgj_finish(jsp); if ((0 == ret) && (res > 0)) ret = res; } return ret; }