aboutsummaryrefslogtreecommitdiff
path: root/src/sg_rep_zones.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_rep_zones.c')
-rw-r--r--src/sg_rep_zones.c1525
1 files changed, 1525 insertions, 0 deletions
diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c
new file mode 100644
index 00000000..c0d19d31
--- /dev/null
+++ b/src/sg_rep_zones.c
@@ -0,0 +1,1525 @@
+/*
+ * Copyright (c) 2014-2022 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <ctype.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_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT
+ * REALMS command to the given SCSI device and decodes the response.
+ * Based on zbc2r12.pdf
+ */
+
+static const char * version_str = "1.42 20220807";
+
+#define MY_NAME "sg_rep_zones"
+
+#define WILD_RZONES_BUFF_LEN (1 << 28)
+#define MAX_RZONES_BUFF_LEN (2 * 1024 * 1024)
+#define DEF_RZONES_BUFF_LEN (1024 * 16)
+#define RCAP16_REPLY_LEN 32
+
+#define SG_ZONING_IN_CMDLEN 16
+#define REPORT_ZONES_DESC_LEN 64
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT 60 /* 60 seconds */
+
+/* Three zone service actions supported by this utility */
+enum zone_report_sa_e {
+ REPORT_ZONES_SA = 0x0,
+ REPORT_REALMS_SA = 0x6,
+ REPORT_ZONE_DOMAINS_SA = 0x7
+};
+
+struct opts_t {
+ bool do_brief;
+ bool do_force;
+ bool do_partial;
+ bool do_raw;
+ bool do_realms;
+ bool do_zdomains;
+ bool maxlen_given;
+ bool o_readonly;
+ bool statistics;
+ bool verbose_given;
+ bool version_given;
+ bool wp_only;
+ enum zone_report_sa_e serv_act;
+ int do_help;
+ int do_hex;
+ int do_num;
+ int find_zt; /* negative values: find first not equal to */
+ int maxlen;
+ int reporting_opt;
+ int vb;
+ uint64_t st_lba;
+ const char * in_fn;
+ sgj_state json_st;
+};
+
+struct zt_num2abbrev_t {
+ int ztn;
+ const char * abbrev;
+};
+
+static struct option long_options[] = {
+ {"brief", no_argument, 0, 'b'}, /* only header and last descriptor */
+ {"domain", no_argument, 0, 'd'},
+ {"domains", no_argument, 0, 'd'},
+ {"force", no_argument, 0, 'f'},
+ {"find", required_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"in", required_argument, 0, 'i'}, /* silent, same as --inhex= */
+ {"inhex", required_argument, 0, 'i'},
+ {"json", optional_argument, 0, 'j'},
+ {"locator", required_argument, 0, 'l'},
+ {"maxlen", required_argument, 0, 'm'},
+ {"num", required_argument, 0, 'n'},
+ {"partial", no_argument, 0, 'p'},
+ {"raw", no_argument, 0, 'r'},
+ {"readonly", no_argument, 0, 'R'},
+ {"realm", no_argument, 0, 'e'},
+ {"realms", no_argument, 0, 'e'},
+ {"report", required_argument, 0, 'o'},
+ {"start", required_argument, 0, 's'},
+ {"statistics", no_argument, 0, 'S'},
+ {"stats", no_argument, 0, 'S'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"wp", no_argument, 0, 'w'},
+ {0, 0, 0, 0},
+};
+
+/* Zone types */
+static struct zt_num2abbrev_t zt_num2abbrev[] = {
+ {0, "none"},
+ {1, "c"}, /* conventionial */
+ {2, "swr"}, /* sequential write required */
+ {3, "swp"}, /* sequential write preferred */
+ {4, "sobr"}, /* sequential or before required */
+ {5, "g"}, /* gap */
+ {-1, NULL}, /* sentinel */
+};
+
+static const char * zn_dnum_s = "zone descriptor number: ";
+
+static const char * meaning_s = "meaning";
+
+
+static void
+prn_zone_type_abbrevs(void)
+{
+ const struct zt_num2abbrev_t * n2ap = zt_num2abbrev;
+ char b[32];
+
+ pr2serr("Zone type number\tAbbreviation\tName\n");
+ pr2serr("----------------\t------------\t----\n");
+ for ( ; n2ap->abbrev; ++n2ap) {
+ if (n2ap == zt_num2abbrev)
+ pr2serr("\t%d\t\t%s\t\t[reserved]\n",
+ n2ap->ztn, n2ap->abbrev);
+ else
+ pr2serr("\t%d\t\t%s\t\t%s\n", n2ap->ztn, n2ap->abbrev,
+ sg_get_zone_type_str(n2ap->ztn, sizeof(b), b));
+ }
+}
+
+static void
+usage(int h)
+{
+ if (h > 1) goto h_twoormore;
+ pr2serr("Usage: "
+ "sg_rep_zones [--domain] [--find=ZT] [--force] [--help] "
+ "[--hex]\n"
+ " [--inhex=FN] [--json[=JO]] "
+ "[--locator=LBA]\n"
+ " [--maxlen=LEN] [--num=NUM] [--partial] "
+ "[--raw]\n"
+ " [--readonly] [--realm] [--report=OPT] "
+ "[--start=LBA]\n"
+ " [--statistics] [--verbose] [--version] "
+ "[--wp]\n"
+ " DEVICE\n");
+ pr2serr(" where:\n"
+ " --domain|-d sends a REPORT ZONE DOMAINS command\n"
+ " --find=ZT|-F ZT find first zone with ZT zone type, "
+ "starting at LBA\n"
+ " if first character of ZT is - or !, "
+ "find first\n"
+ " zone that is not ZT\n"
+ " --force|-f bypass some sanity checks when decoding "
+ "response\n"
+ " --help|-h print out usage message, use twice for "
+ "more help\n"
+ " --hex|-H output response in hexadecimal; used "
+ "twice\n"
+ " shows decoded values in hex\n"
+ " --inhex=FN|-i FN decode contents of FN, ignore DEVICE\n"
+ " --json[=JO]|-j[JO] output in JSON instead of human "
+ "readable text.\n"
+ " Use --json=? for JSON help\n"
+ " --locator=LBA|-l LBA similar to --start= option\n"
+ " --maxlen=LEN|-m LEN max response length (allocation "
+ "length in cdb)\n"
+ " (def: 0 -> 8192 bytes)\n"
+ " --num=NUM|-n NUM number of zones to output (def: 0 -> "
+ "all)\n"
+ " --partial|-p sets PARTIAL bit in cdb (def: 0 -> "
+ "zone list\n"
+ " length not altered by allocation length "
+ "in cdb)\n"
+ " --raw|-r output response in binary\n"
+ " --readonly|-R open DEVICE read-only (def: read-write)\n"
+ " --realm|-e sends a REPORT REALMS command\n"
+ " --report=OPT|-o OP reporting options (def: 0: all "
+ "zones)\n"
+ " --start=LBA|-s LBA report zones from the LBA (def: 0)\n"
+ " need not be a zone starting LBA\n"
+ " --statistics|-S gather statistics by reviewing zones\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n"
+ " --wp|-w output write pointer only\n\n"
+ "Sends a SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT REALMS "
+ "command.\nBy default sends a REPORT ZONES command. Give help "
+ "option twice\n(e.g. '-hh') to see reporting options "
+ "enumerated.\n");
+ return;
+h_twoormore:
+ pr2serr("Reporting options for REPORT ZONES:\n"
+ " 0x0 list all zones\n"
+ " 0x1 list zones with a zone condition of EMPTY\n"
+ " 0x2 list zones with a zone condition of IMPLICITLY "
+ "OPENED\n"
+ " 0x3 list zones with a zone condition of EXPLICITLY "
+ "OPENED\n"
+ " 0x4 list zones with a zone condition of CLOSED\n"
+ " 0x5 list zones with a zone condition of FULL\n"
+ " 0x6 list zones with a zone condition of READ ONLY\n"
+ " 0x7 list zones with a zone condition of OFFLINE\n"
+ " 0x8 list zones with a zone condition of INACTIVE\n"
+ " 0x10 list zones with RWP Recommended set to true\n"
+ " 0x11 list zones with Non-sequential write resources "
+ "active set to true\n"
+ " 0x3e list zones except those with zone type: GAP\n"
+ " 0x3f list zones with a zone condition of NOT WRITE "
+ "POINTER\n\n");
+ pr2serr("Reporting options for REPORT ZONE DOMAINS:\n"
+ " 0x0 list all zone domains\n"
+ " 0x1 list all zone domains in which all zones are active\n"
+ " 0x2 list all zone domains that contain active zones\n"
+ " 0x3 list all zone domains that do not contain any active "
+ "zones\n\n");
+ pr2serr("Reporting options for REPORT REALMS:\n"
+ " 0x0 list all realms\n"
+ " 0x1 list all realms that contain active Sequential Or "
+ "Before Required zones\n"
+ " 0x2 list all realms that contain active Sequential Write "
+ "Required zones\n"
+ " 0x3 list all realms that contain active Sequential Write "
+ "Preferred zones\n");
+ pr2serr("\n");
+ prn_zone_type_abbrevs();
+}
+
+/* Invokes a SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT REALMS command
+ * (see ZBC and ZBC-2). Return of 0 -> success, various SG_LIB_CAT_* positive
+ * values or -1 -> other errors */
+static int
+sg_ll_report_zzz(int sg_fd, enum zone_report_sa_e serv_act, uint64_t zs_lba,
+ bool partial, int report_opts, void * resp, int mx_resp_len,
+ int * residp, bool noisy, int vb)
+{
+ int ret, res, sense_cat;
+ uint8_t rz_cdb[SG_ZONING_IN_CMDLEN] =
+ {SG_ZONING_IN, REPORT_ZONES_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0};
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+ struct sg_pt_base * ptvp;
+
+ rz_cdb[1] = (uint8_t)serv_act;
+ sg_put_unaligned_be64(zs_lba, rz_cdb + 2);
+ sg_put_unaligned_be32((uint32_t)mx_resp_len, rz_cdb + 10);
+ rz_cdb[14] = report_opts & 0x3f;
+ if (partial)
+ rz_cdb[14] |= 0x80;
+ if (vb) {
+ char b[128];
+
+ pr2serr(" %s\n", sg_get_command_str(rz_cdb, SG_ZONING_IN_CMDLEN,
+ true, sizeof(b), b));
+ }
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ pr2serr("%s: out of memory\n", __func__);
+ return -1;
+ }
+ set_scsi_pt_cdb(ptvp, rz_cdb, sizeof(rz_cdb));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
+ res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb);
+ ret = sg_cmds_process_resp(ptvp, "report zone/domain/realm", res, noisy,
+ vb, &sense_cat);
+ if (-1 == ret) {
+ if (get_scsi_pt_transport_err(ptvp))
+ ret = SG_LIB_TRANSPORT_ERROR;
+ else
+ ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
+ } else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else
+ ret = 0;
+ if (residp)
+ *residp = get_scsi_pt_resid(ptvp);
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+static void
+dStrRaw(const uint8_t * str, int len)
+{
+ int k;
+
+ for (k = 0; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+static const char *
+zone_condition_str(int zc, char * b, int blen, int vb)
+{
+ const char * cp;
+
+ if (NULL == b)
+ return "zone_condition_str: NULL ptr)";
+ switch (zc) {
+ case 0:
+ cp = "Not write pointer";
+ break;
+ case 1:
+ cp = "Empty";
+ break;
+ case 2:
+ cp = "Implicitly opened";
+ break;
+ case 3:
+ cp = "Explicitly opened";
+ break;
+ case 4:
+ cp = "Closed";
+ break;
+ case 5:
+ cp = "Inactive";
+ break;
+ case 0xd:
+ cp = "Read only";
+ break;
+ case 0xe:
+ cp = "Full";
+ break;
+ case 0xf:
+ cp = "Offline";
+ break;
+ default:
+ cp = NULL;
+ break;
+ }
+ if (cp) {
+ if (vb)
+ snprintf(b, blen, "%s [0x%x]", cp, zc);
+ else
+ snprintf(b, blen, "%s", cp);
+ } else
+ snprintf(b, blen, "Reserved [0x%x]", zc);
+ return b;
+}
+
+static const char * same_desc_arr[16] = {
+ "zone type and length may differ in each descriptor",
+ "zone type and length same in each descriptor",
+ "zone type and length same apart from length in last descriptor",
+ "zone type for each descriptor may be different",
+ "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
+ "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
+ "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+static uint64_t
+prt_a_zn_desc(const uint8_t *bp, const struct opts_t * op,
+ sgj_state * jsp, sgj_opaque_p jop)
+{
+ uint8_t zt, zc;
+ uint64_t lba, len, wp;
+ char b[80];
+
+ jop = jop ? jop : jsp->basep;
+ zt = bp[0] & 0xf;
+ zc = (bp[1] >> 4) & 0xf;
+ sg_get_zone_type_str(zt, sizeof(b), b);
+ sgj_pr_hr(jsp, " Zone type: %s\n", b);
+ sgj_js_nv_istr(jsp, jop, "zone_type", zt, meaning_s, b);
+ zone_condition_str(zc, b, sizeof(b), op->vb);
+ sgj_pr_hr(jsp, " Zone condition: %s\n", b);
+ sgj_js_nv_istr(jsp, jop, "zone_condition", zc, meaning_s, b);
+ sgj_haj_vi(jsp, jop, 3, "PUEP", SGJ_SEP_COLON_1_SPACE,
+ !!(bp[1] & 0x4), false);
+ sgj_haj_vi(jsp, jop, 3, "NON_SEQ", SGJ_SEP_COLON_1_SPACE,
+ !!(bp[1] & 0x2), false);
+ sgj_haj_vi(jsp, jop, 3, "RESET", SGJ_SEP_COLON_1_SPACE,
+ !!(bp[1] & 0x1), false);
+ len = sg_get_unaligned_be64(bp + 8);
+ sgj_pr_hr(jsp, " Zone Length: 0x%" PRIx64 "\n", len);
+ sgj_js_nv_ihex(jsp, jop, "zone_length", (int64_t)len);
+ lba = sg_get_unaligned_be64(bp + 16);
+ sgj_pr_hr(jsp, " Zone start LBA: 0x%" PRIx64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jop, "zone_start_lba", (int64_t)lba);
+ wp = sg_get_unaligned_be64(bp + 24);
+ if (sg_all_ffs((const uint8_t *)&wp, sizeof(wp)))
+ sgj_pr_hr(jsp, " Write pointer LBA: -1\n");
+ else
+ sgj_pr_hr(jsp, " Write pointer LBA: 0x%" PRIx64 "\n", wp);
+ sgj_js_nv_ihex(jsp, jop, "write_pointer_lba", (int64_t)wp);
+ return lba + len;
+}
+
+static int
+decode_rep_zones(const uint8_t * rzBuff, int act_len, uint32_t decod_len,
+ const struct opts_t * op, sgj_state * jsp)
+{
+ bool as_json = jsp ? jsp->pr_as_json : false;
+ int k, same, num_zd;
+ uint64_t wp, ul, mx_lba;
+ sgj_opaque_p jop = jsp ? jsp->basep : NULL;
+ sgj_opaque_p jap = NULL;
+ const uint8_t * bp;
+
+ if ((uint32_t)act_len < decod_len) {
+ num_zd = (act_len >= 64) ? ((act_len - 64) / REPORT_ZONES_DESC_LEN)
+ : 0;
+ if (act_len == op->maxlen) {
+ if (op->maxlen_given)
+ pr2serr("decode length [%u bytes] may be constrained by "
+ "given --maxlen value, try increasing\n", decod_len);
+ else
+ pr2serr("perhaps --maxlen=%u needs to be used\n", decod_len);
+ } else if (op->in_fn)
+ pr2serr("perhaps %s has been truncated\n", op->in_fn);
+ } else
+ num_zd = (decod_len - 64) / REPORT_ZONES_DESC_LEN;
+ same = rzBuff[4] & 0xf;
+ mx_lba = sg_get_unaligned_be64(rzBuff + 8);
+ if (op->wp_only) {
+ ;
+ } else if (op->do_hex) {
+ hex2stdout(rzBuff, 64, -1);
+ printf("\n");
+ } else {
+ uint64_t rzslbag = sg_get_unaligned_be64(rzBuff + 16);
+ static const char * rzslbag_s = "Reported zone starting LBA "
+ "granularity";
+
+ sgj_pr_hr(jsp, " Same=%d: %s\n", same, same_desc_arr[same]);
+ sgj_js_nv_istr(jsp, jop, "same", same, meaning_s,
+ same_desc_arr[same]);
+ sgj_pr_hr(jsp, " Maximum LBA: 0x%" PRIx64 "\n\n", mx_lba);
+ sgj_js_nv_ihex(jsp, jop, "maximum_lba", mx_lba);
+ sgj_pr_hr(jsp, " %s: 0x%" PRIx64 "\n\n", rzslbag_s, rzslbag);
+ sgj_js_nv_ihex(jsp, jop, rzslbag_s, rzslbag);
+ }
+ if (op->do_num > 0)
+ num_zd = (num_zd > op->do_num) ? op->do_num : num_zd;
+ if (((uint32_t)act_len < decod_len) &&
+ ((num_zd * REPORT_ZONES_DESC_LEN) + 64 > act_len)) {
+ pr2serr("Skip due to truncated response, try using --num= to a "
+ "value less than %d\n", num_zd);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ if (op->do_brief && (num_zd > 0)) {
+ bp = rzBuff + 64 + ((num_zd - 1) * REPORT_ZONES_DESC_LEN);
+ if (op->do_hex) {
+ if (op->wp_only)
+ hex2stdout(bp + 24, 8, -1);
+ else
+ hex2stdout(bp, 64, -1);
+ return 0;
+ }
+ sgj_pr_hr(jsp, "From last descriptor in this response:\n");
+ sgj_pr_hr(jsp, " %s%d\n", zn_dnum_s, num_zd - 1);
+ sgj_js_nv_i(jsp, jop, "zone_descriptor_index", num_zd - 1);
+ ul = prt_a_zn_desc(bp, op, jsp, jop);
+ if (ul > mx_lba)
+ sgj_pr_hr(jsp, " >> This zone seems to be the last one\n");
+ else
+ sgj_pr_hr(jsp, " >> Probable next Zone start LBA: 0x%" PRIx64
+ "\n", ul);
+ return 0;
+ }
+ if (as_json)
+ jap = sgj_named_subarray_r(jsp, NULL, "zone_descriptors_list");
+ for (k = 0, bp = rzBuff + 64; k < num_zd;
+ ++k, bp += REPORT_ZONES_DESC_LEN) {
+ sgj_opaque_p jo2p;
+
+ if (! op->wp_only)
+ sgj_pr_hr(jsp, " %s%d\n", zn_dnum_s, k);
+ if (op->do_hex) {
+ hex2stdout(bp, 64, -1);
+ continue;
+ }
+ if (op->wp_only) {
+ if (op->do_hex)
+ hex2stdout(bp + 24, 8, -1);
+ else {
+ wp = sg_get_unaligned_be64(bp + 24);
+ if (sg_all_ffs((const uint8_t *)&wp, sizeof(wp)))
+ sgj_pr_hr(jsp, "-1\n");
+ else
+ sgj_pr_hr(jsp, "0x%" PRIx64 "\n", wp);
+ jo2p = sgj_new_unattached_object_r(jsp);
+ sgj_js_nv_ihex(jsp, jo2p, "write_pointer_lba", (int64_t)wp);
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ continue;
+ }
+ jo2p = sgj_new_unattached_object_r(jsp);
+ prt_a_zn_desc(bp, op, jsp, jo2p);
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ if ((op->do_num == 0) && (! op->wp_only) && (! op->do_hex)) {
+ if ((64 + (REPORT_ZONES_DESC_LEN * (uint32_t)num_zd)) < decod_len)
+ sgj_pr_hr(jsp, "\n>>> Beware: Zone list truncated, may need "
+ "another call\n");
+ }
+ return 0;
+}
+
+static int
+decode_rep_realms(const uint8_t * rzBuff, int act_len,
+ const struct opts_t * op, sgj_state * jsp)
+{
+ uint32_t k, realms_count, derived_realms_count, r_desc_len,
+ zdomains_count;
+ uint64_t nr_locator;
+ const uint8_t * bp;
+ sgj_opaque_p jop = jsp ? jsp->basep : NULL;
+ sgj_opaque_p jap = NULL;
+ sgj_opaque_p ja2p = NULL;
+
+ if (act_len < 12) {
+ pr2serr("need more than 12 bytes to decode, got %u\n", act_len);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ realms_count = sg_get_unaligned_be32(rzBuff + 4);
+ r_desc_len = sg_get_unaligned_be32(rzBuff + 8);
+ if (act_len < 20)
+ nr_locator = sg_get_unaligned_be64(rzBuff + 12);
+ else
+ nr_locator = 0;
+ sgj_haj_vi(jsp, jop, 0, "Realms_count", SGJ_SEP_EQUAL_NO_SPACE,
+ realms_count, true);
+ sgj_haj_vi(jsp, jop, 0, "Realms_descriptor_length",
+ SGJ_SEP_EQUAL_NO_SPACE, r_desc_len, true);
+ sgj_pr_hr(jsp, "Next_realm_locator=0x%" PRIx64 "\n", nr_locator);
+ sgj_js_nv_ihex(jsp, jop, "Next_realm_locator", nr_locator);
+ if ((realms_count < 1) || (act_len < (64 + 16)) || (r_desc_len < 16)) {
+ if (op->vb) {
+ pr2serr("%s: exiting early because ", __func__);
+ if (realms_count < 1)
+ pr2serr("realms_count is zero\n");
+ else if (r_desc_len < 16)
+ pr2serr("realms descriptor length less than 16\n");
+ else
+ pr2serr("actual_length (%u) too short\n", act_len);
+ }
+ return 0;
+ }
+ derived_realms_count = (act_len - 64) / r_desc_len;
+ if (derived_realms_count > realms_count) {
+ if (op->vb)
+ pr2serr("%s: derived_realms_count [%u] > realms_count [%u]\n",
+ __func__, derived_realms_count, realms_count);
+ } else if (derived_realms_count < realms_count) {
+ if (op->vb)
+ pr2serr("%s: derived_realms_count [%u] < realms_count [%u], "
+ "use former\n", __func__, derived_realms_count,
+ realms_count);
+ realms_count = derived_realms_count;
+ }
+ zdomains_count = (r_desc_len - 16) / 16;
+
+ if (op->do_num > 0)
+ realms_count = (realms_count > (uint32_t)op->do_num) ?
+ (uint32_t)op->do_num : realms_count;
+ jap = sgj_named_subarray_r(jsp, jop, "realm_descriptors_list");
+
+ for (k = 0, bp = rzBuff + 64; k < realms_count; ++k, bp += r_desc_len) {
+ uint32_t j;
+ uint16_t restrictions;
+ const uint8_t * zp;
+ sgj_opaque_p jo2p;
+
+ jo2p = sgj_new_unattached_object_r(jsp);
+ sgj_haj_vi(jsp, jo2p, 1, "Realms_id", SGJ_SEP_EQUAL_NO_SPACE,
+ sg_get_unaligned_be32(bp + 0), true);
+ if (op->do_hex) {
+ hex2stdout(bp, r_desc_len, -1);
+ continue;
+ }
+ restrictions = sg_get_unaligned_be16(bp + 4);
+ sgj_pr_hr(jsp, " realm_restrictions=0x%hu\n", restrictions);
+ sgj_js_nv_ihex(jsp, jo2p, "realm_restrictions", restrictions);
+ sgj_haj_vi(jsp, jo2p, 3, "active_zone_domain_id",
+ SGJ_SEP_EQUAL_NO_SPACE, bp[7], true);
+
+ ja2p = sgj_named_subarray_r(jsp, jo2p,
+ "realm_start_end_descriptors_list");
+ for (j = 0, zp = bp + 16; j < zdomains_count; ++j, zp += 16) {
+ uint64_t lba;
+ sgj_opaque_p jo3p;
+
+ jo3p = sgj_new_unattached_object_r(jsp);
+ sgj_pr_hr(jsp, " zone_domain=%u\n", j);
+ sgj_js_nv_i(jsp, jo3p, "corresponding_zone_domain_id", j);
+ lba = sg_get_unaligned_be64(zp + 0);
+ sgj_pr_hr(jsp, " starting_lba=0x%" PRIx64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jo3p, "realm_starting_lba", (int64_t)lba);
+ lba = sg_get_unaligned_be64(zp + 8);
+ sgj_pr_hr(jsp, " ending_lba=0x%" PRIx64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jo3p, "realm_ending_lba", (int64_t)lba);
+ sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
+ }
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ return 0;
+}
+
+static int
+decode_rep_zdomains(const uint8_t * rzBuff, int act_len,
+ const struct opts_t * op, sgj_state * jsp)
+{
+ uint32_t k, zd_len, zd_ret_len, zdoms_sup, zdoms_rep, zd_rep_opts;
+ uint32_t num, der_zdoms;
+ uint64_t zd_locator;
+ sgj_opaque_p jop = jsp ? jsp->basep : NULL;
+ sgj_opaque_p jap = NULL;
+ const uint8_t * bp;
+
+ if (act_len < 12) {
+ pr2serr("need more than 12 bytes to decode, got %u\n", act_len);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ zd_len = sg_get_unaligned_be32(rzBuff + 0);
+ zd_ret_len = sg_get_unaligned_be32(rzBuff + 4);
+ zdoms_sup = rzBuff[8];
+ zdoms_rep = rzBuff[9];
+ zd_rep_opts = rzBuff[10];
+ if (act_len < 24)
+ zd_locator = sg_get_unaligned_be64(rzBuff + 16);
+ else
+ zd_locator = 0;
+ sgj_haj_vi(jsp, jop, 0, "Zone_domains_returned_list_length=",
+ SGJ_SEP_EQUAL_NO_SPACE, zd_ret_len, true);
+ sgj_haj_vi(jsp, jop, 0, "Zone_domains_supported",
+ SGJ_SEP_EQUAL_NO_SPACE, zdoms_sup, true);
+ sgj_haj_vi(jsp, jop, 0, "Zone_domains_reported",
+ SGJ_SEP_EQUAL_NO_SPACE, zdoms_rep, true);
+ sgj_pr_hr(jsp, "Reporting_options=0x%x\n", zd_rep_opts);
+ sgj_js_nv_ihex(jsp, jop, "Reporting_options", zd_rep_opts);
+ sgj_pr_hr(jsp, "Zone_domain_locator=0x%" PRIx64 "\n", zd_locator);
+ sgj_js_nv_ihex(jsp, jop, "Zone_domain_locator", zd_locator);
+
+ der_zdoms = zd_len / 96;
+ if (op->vb > 1)
+ pr2serr("Derived zdomains=%u\n", der_zdoms);
+ num = ((der_zdoms < zdoms_rep) ? der_zdoms : zdoms_rep) * 96;
+ jap = sgj_named_subarray_r(jsp, jop, "zone_domain_descriptors_list");
+
+ for (k = 0, bp = rzBuff + 64; k < num; k += 96, bp += 96) {
+ uint64_t lba;
+ sgj_opaque_p jo2p;
+
+ jo2p = sgj_new_unattached_object_r(jsp);
+ sgj_haj_vi(jsp, jo2p, 3, "zone_domain",
+ SGJ_SEP_EQUAL_NO_SPACE, bp[0], true);
+ lba = sg_get_unaligned_be64(bp + 16);
+ sgj_pr_hr(jsp, " zone_count=%" PRIu64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jo2p, "zone_count", lba);
+ lba = sg_get_unaligned_be64(bp + 24);
+ sgj_pr_hr(jsp, " starting_lba=0x%" PRIx64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jo2p, "starting_lba", lba);
+ lba = sg_get_unaligned_be64(bp + 32);
+ sgj_pr_hr(jsp, " ending_lba=0x%" PRIx64 "\n", lba);
+ sgj_js_nv_ihex(jsp, jo2p, "ending_lba", lba);
+ sgj_pr_hr(jsp, " zone_domain_zone_type=0x%x\n", bp[40]);
+ sgj_js_nv_ihex(jsp, jo2p, "zone_domain_zone_type", bp[40]);
+ sgj_haj_vi(jsp, jo2p, 5, "VZDZT", SGJ_SEP_EQUAL_NO_SPACE,
+ !!(0x2 & bp[42]), false);
+ sgj_haj_vi(jsp, jo2p, 5, "SRB", SGJ_SEP_EQUAL_NO_SPACE,
+ !!(0x1 & bp[42]), false);
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ return 0;
+}
+
+static int
+find_report_zones(int sg_fd, uint8_t * rzBuff, const char * cmd_name,
+ struct opts_t * op, sgj_state * jsp)
+{
+ bool as_json = (jsp && (0 == op->do_hex)) ? jsp->pr_as_json : false;
+ bool found = false;
+ uint8_t zt;
+ int k, res, resid, rlen, num_zd, num_rem;
+ uint32_t zn_dnum = 0;
+ uint64_t slba = op->st_lba;
+ uint64_t mx_lba = 0;
+ const uint8_t * bp = rzBuff;
+ char b[96];
+
+ num_rem = op->do_num ? op->do_num : INT_MAX;
+ for ( ; num_rem > 0; num_rem -= num_zd) {
+ resid = 0;
+ if (sg_fd >= 0) {
+ res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba,
+ true /* set partial */, op->reporting_opt,
+ rzBuff, op->maxlen, &resid, true, op->vb);
+ if (res) {
+ if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr("%s: %s%u, %s command not supported\n", __func__,
+ zn_dnum_s, zn_dnum, cmd_name);
+ else {
+ sg_get_category_sense_str(res, sizeof(b), b, op->vb);
+ pr2serr("%s: %s%u, %s command: %s\n", __func__,
+ zn_dnum_s, zn_dnum, cmd_name, b);
+ }
+ break;
+ }
+ } else
+ res = 0;
+ rlen = op->maxlen - resid;
+ if (rlen <= 64)
+ break;
+ mx_lba = sg_get_unaligned_be64(rzBuff + 8);
+ num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN;
+ if (num_zd > num_rem)
+ num_zd = num_rem;
+ for (k = 0, bp = rzBuff + 64; k < num_zd;
+ ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) {
+ zt = 0xf & bp[0];
+ if (op->find_zt > 0) {
+ if ((uint8_t)op->find_zt == zt )
+ break;
+ } else if (op->find_zt < 0) {
+ if ((uint8_t)(-op->find_zt) != zt )
+ break;
+ }
+ slba = sg_get_unaligned_be64(bp + 16) +
+ sg_get_unaligned_be64(bp + 8);
+ }
+ if (k < num_zd) {
+ found = true;
+ break;
+ } else if ((slba > mx_lba) || (sg_fd < 0))
+ break;
+ } /* end of outer for loop */
+ if (res == 0) {
+ sgj_opaque_p jo2p = as_json ?
+ sgj_named_subobject_r(jsp, NULL, "find_condition") : NULL;
+
+ if (found) {
+ if (op->do_hex) {
+ hex2stdout(rzBuff, 64, -1);
+ printf("\n");
+ hex2stdout(bp, 64, -1);
+ } else {
+ sgj_pr_hr(jsp, "Condition met at:\n");
+ sgj_pr_hr(jsp, " %s: %d\n", zn_dnum_s, zn_dnum);
+ sgj_js_nv_b(jsp, jo2p, "met", true);
+ sgj_js_nv_i(jsp, jo2p, "zone_descriptor_index", zn_dnum);
+ prt_a_zn_desc(bp, op, jsp, jo2p);
+ }
+ } else {
+ if (op->do_hex) {
+ memset(b, 0xff, 64);
+ hex2stdout((const uint8_t *)b, 64, -1);
+ } else {
+ sgj_js_nv_b(jsp, jo2p, "met", false);
+ sgj_js_nv_i(jsp, jo2p, "zone_descriptor_index", zn_dnum);
+ if (num_rem < 1)
+ sgj_pr_hr(jsp, "Condition NOT met, checked %d zones; "
+ "next %s%u\n", op->do_num, zn_dnum_s, zn_dnum);
+ else
+ sgj_pr_hr(jsp, "Condition NOT met; next %s%u\n",
+ zn_dnum_s, zn_dnum);
+ }
+ }
+ }
+ return res;
+}
+
+struct statistics_t {
+ uint32_t zt_conv_num;
+ uint32_t zt_swr_num;
+ uint32_t zt_swp_num;
+ uint32_t zt_sob_num;
+ uint32_t zt_gap_num;
+ uint32_t zt_unk_num;
+
+ uint32_t zc_nwp_num;
+ uint32_t zc_mt_num;
+ uint32_t zc_iop_num;
+ uint32_t zc_eop_num;
+ uint32_t zc_cl_num;
+ uint32_t zc_ina_num;
+ uint32_t zc_ro_num;
+ uint32_t zc_full_num;
+ uint32_t zc_off_num;
+ uint32_t zc_unk_num;
+
+ /* The following LBAs have 1 added to them, initialized to 0 */
+ uint64_t zt_swr_1st_lba1;
+ uint64_t zt_swp_1st_lba1;
+ uint64_t zt_sob_1st_lba1;
+ uint64_t zt_gap_1st_lba1;
+
+ uint64_t zc_nwp_1st_lba1;
+ uint64_t zc_mt_1st_lba1;
+ uint64_t zc_iop_1st_lba1;
+ uint64_t zc_eop_1st_lba1;
+ uint64_t zc_cl_1st_lba1;
+ uint64_t zc_ina_1st_lba1;
+ uint64_t zc_ro_1st_lba1;
+ uint64_t zc_full_1st_lba1;
+ uint64_t zc_off_1st_lba1;
+
+ uint64_t wp_max_lba1; /* ... that isn't Zone start LBA */
+ uint64_t wp_blk_num; /* sum of (zwp - zs_lba) */
+ uint64_t conv_blk_num; /* sum of (z_blks) of zt=conv */
+};
+
+static int
+gather_statistics(int sg_fd, uint8_t * rzBuff, const char * cmd_name,
+ struct opts_t * op)
+{
+ uint8_t zt, zc;
+ int k, res, resid, rlen, num_zd, num_rem;
+ uint32_t zn_dnum = 0;
+ uint64_t slba = op->st_lba;
+ uint64_t mx_lba = 0;
+ uint64_t zs_lba, zwp, z_blks;
+ const uint8_t * bp = rzBuff;
+ struct statistics_t st SG_C_CPP_ZERO_INIT;
+ char b[96];
+
+ if (op->serv_act != REPORT_ZONES_SA) {
+ pr2serr("%s: do not support statistics for %s yet\n", __func__,
+ cmd_name);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ num_rem = op->do_num ? op->do_num : INT_MAX;
+ for ( ; num_rem > 0; num_rem -= num_zd) {
+ resid = 0;
+ zs_lba = slba;
+ if (sg_fd >= 0) {
+ res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba,
+ true /* set partial */, op->reporting_opt,
+ rzBuff, op->maxlen, &resid, true, op->vb);
+ if (res) {
+ if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr("%s: %s%u, %s command not supported\n", __func__,
+ zn_dnum_s, zn_dnum, cmd_name);
+ else {
+ sg_get_category_sense_str(res, sizeof(b), b, op->vb);
+ pr2serr("%s: %s%u, %s command: %s\n", __func__,
+ zn_dnum_s, zn_dnum, cmd_name, b);
+ }
+ break;
+ }
+ } else
+ res = 0;
+ rlen = op->maxlen - resid;
+ if (rlen <= 64) {
+ break;
+ }
+ mx_lba = sg_get_unaligned_be64(rzBuff + 8);
+ num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN;
+ if (num_zd > num_rem)
+ num_zd = num_rem;
+ for (k = 0, bp = rzBuff + 64; k < num_zd;
+ ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) {
+ z_blks = sg_get_unaligned_be64(bp + 8);
+ zs_lba = sg_get_unaligned_be64(bp + 16);
+ zwp = sg_get_unaligned_be64(bp + 24);
+ zt = 0xf & bp[0];
+ switch (zt) {
+ case 1: /* conventional */
+ ++st.zt_conv_num;
+ st.conv_blk_num += z_blks;
+ break;
+ case 2: /* sequential write required */
+ ++st.zt_swr_num;
+ if (0 == st.zt_swr_1st_lba1)
+ st.zt_swr_1st_lba1 = zs_lba + 1;
+ break;
+ case 3: /* sequential write preferred */
+ ++st.zt_swp_num;
+ if (0 == st.zt_swp_1st_lba1)
+ st.zt_swp_1st_lba1 = zs_lba + 1;
+ break;
+ case 4: /* sequential or before (write) */
+ ++st.zt_sob_num;
+ if (0 == st.zt_sob_1st_lba1)
+ st.zt_sob_1st_lba1 = zs_lba + 1;
+ break;
+ case 5: /* gap */
+ ++st.zt_gap_num;
+ if (0 == st.zt_gap_1st_lba1)
+ st.zt_gap_1st_lba1 = zs_lba + 1;
+ break;
+ default:
+ ++st.zt_unk_num;
+ break;
+ }
+ zc = (bp[1] >> 4) & 0xf;
+ switch (zc) {
+ case 0: /* not write pointer (zone) */
+ ++st.zc_nwp_num;
+ if (0 == st.zc_nwp_1st_lba1)
+ st.zc_nwp_1st_lba1 = zs_lba + 1;
+ break;
+ case 1: /* empty */
+ ++st.zc_mt_num;
+ if (0 == st.zc_mt_1st_lba1)
+ st.zc_mt_1st_lba1 = zs_lba + 1;
+ break;
+ case 2: /* implicitly opened */
+ ++st.zc_iop_num;
+ if (0 == st.zc_iop_1st_lba1)
+ st.zc_iop_1st_lba1 = zs_lba + 1;
+ if (zwp > zs_lba) {
+ st.wp_max_lba1 = zwp + 1;
+ st.wp_blk_num += zwp - zs_lba;
+ }
+ break;
+ case 3: /* explicitly opened */
+ ++st.zc_eop_num;
+ if (0 == st.zc_eop_1st_lba1)
+ st.zc_eop_1st_lba1 = zs_lba + 1;
+ if (zwp > zs_lba) {
+ st.wp_max_lba1 = zwp + 1;
+ st.wp_blk_num += zwp - zs_lba;
+ }
+ break;
+ case 4: /* closed */
+ ++st.zc_cl_num;
+ if (0 == st.zc_cl_1st_lba1)
+ st.zc_cl_1st_lba1 = zs_lba + 1;
+ if (zwp > zs_lba) {
+ st.wp_max_lba1 = zwp + 1;
+ st.wp_blk_num += zwp - zs_lba;
+ }
+ break;
+ case 5: /* inactive */
+ ++st.zc_ina_num;
+ if (0 == st.zc_ina_1st_lba1)
+ st.zc_ina_1st_lba1 = zs_lba + 1;
+ break;
+ case 0xd: /* read-only */
+ ++st.zc_ro_num;
+ if (0 == st.zc_ro_1st_lba1)
+ st.zc_ro_1st_lba1 = zs_lba + 1;
+ break;
+ case 0xe: /* full */
+ ++st.zc_full_num;
+ if (0 == st.zc_full_1st_lba1)
+ st.zc_full_1st_lba1 = zs_lba + 1;
+ st.wp_blk_num += z_blks;
+ break;
+ case 0xf: /* offline */
+ ++st.zc_off_num;
+ if (0 == st.zc_off_1st_lba1)
+ st.zc_off_1st_lba1 = zs_lba + 1;
+ break;
+ default:
+ ++st.zc_unk_num;
+ break;
+ }
+ slba = zs_lba + z_blks;
+ } /* end of inner for loop */
+ if ((slba > mx_lba) || (sg_fd < 0))
+ break;
+ } /* end of outer for loop */
+ printf("Number of conventional type zones: %u\n", st.zt_conv_num);
+ if (st.zt_swr_num > 0)
+ printf("Number of sequential write required type zones: %u\n",
+ st.zt_swr_num);
+ if (st.zt_swr_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zt_swr_1st_lba1 - 1);
+ if (st.zt_swp_num > 0)
+ printf("Number of sequential write preferred type zones: %u\n",
+ st.zt_swp_num);
+ if (st.zt_swp_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zt_swp_1st_lba1 - 1);
+ if (st.zt_sob_num > 0)
+ printf("Number of sequential or before type zones: %u\n",
+ st.zt_sob_num);
+ if (st.zt_sob_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zt_sob_1st_lba1 - 1);
+ if (st.zt_gap_num > 0)
+ printf("Number of gap type zones: %u\n", st.zt_gap_num);
+ if (st.zt_gap_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zt_gap_1st_lba1 - 1);
+ if (st.zt_unk_num > 0)
+ printf("Number of unknown type zones: %u\n", st.zt_unk_num);
+
+ printf("Number of 'not write pointer' condition zones: %u\n",
+ st.zc_nwp_num);
+ if (st.zc_nwp_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_nwp_1st_lba1 - 1);
+ printf("Number of empty condition zones: %u\n", st.zc_mt_num);
+ if (st.zc_mt_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_mt_1st_lba1 - 1);
+ if (st.zc_iop_num > 0)
+ printf("Number of implicitly open condition zones: %u\n",
+ st.zc_iop_num);
+ if (st.zc_iop_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_iop_1st_lba1 - 1);
+ if (st.zc_eop_num)
+ printf("Number of explicitly open condition zones: %u\n",
+ st.zc_eop_num);
+ if (st.zc_eop_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_eop_1st_lba1 - 1);
+ if (st.zc_cl_num)
+ printf("Number of closed condition zones: %u\n", st.zc_cl_num);
+ if (st.zc_cl_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_cl_1st_lba1 - 1);
+ if (st.zc_ina_num)
+ printf("Number of inactive condition zones: %u\n", st.zc_ina_num);
+ if (st.zc_ina_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_ina_1st_lba1 - 1);
+ if (st.zc_ro_num)
+ printf("Number of inactive condition zones: %u\n", st.zc_ro_num);
+ if (st.zc_ro_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_ro_1st_lba1 - 1);
+ if (st.zc_full_num)
+ printf("Number of full condition zones: %u\n", st.zc_full_num);
+ if (st.zc_full_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_full_1st_lba1 - 1);
+ if (st.zc_off_num)
+ printf("Number of offline condition zones: %u\n", st.zc_off_num);
+ if (st.zc_off_1st_lba1 > 0)
+ printf(" Lowest starting LBA: 0x%" PRIx64 "\n",
+ st.zc_off_1st_lba1 - 1);
+ if (st.zc_unk_num > 0)
+ printf("Number of unknown condition zones: %u\n", st.zc_unk_num);
+
+ if (st.wp_max_lba1 > 0)
+ printf("Highest active write pointer LBA: 0x%" PRIx64 "\n",
+ st.wp_max_lba1 - 1);
+ printf("Number of used blocks in write pointer zones: 0x%" PRIx64 "\n",
+ st.wp_blk_num);
+
+ if ((sg_fd >= 0) && (op->maxlen >= RCAP16_REPLY_LEN) &&
+ ((st.wp_blk_num > 0) || (st.conv_blk_num > 0))) {
+ uint32_t block_size = 0;
+ uint64_t total_sz;
+ double sz_mb, sz_gb;
+
+ res = sg_ll_readcap_16(sg_fd, false, 0, rzBuff,
+ RCAP16_REPLY_LEN, true, op->vb);
+ if (SG_LIB_CAT_INVALID_OP == res) {
+ pr2serr("READ CAPACITY (16) cdb not supported\n");
+ } 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, sizeof(b), b, op->vb);
+ pr2serr("READ CAPACITY (16) failed: %s\n", b);
+ } else
+ block_size = sg_get_unaligned_be32(rzBuff + 8);
+
+ if (st.wp_blk_num) {
+ total_sz = st.wp_blk_num * block_size;
+ sz_mb = (double)(total_sz) / (double)(1048576);
+ sz_gb = (double)(total_sz) / (double)(1000000000L);
+#ifdef SG_LIB_MINGW
+ printf(" associated size: %" PRIu64 " bytes, %g MiB, %g GB",
+ total_sz, sz_mb, sz_gb);
+#else
+ printf(" associated size: %" PRIu64 " bytes, %.1f MiB, %.2f "
+ "GB", total_sz, sz_mb, sz_gb);
+#endif
+ if (sz_gb > 2000) {
+#ifdef SG_LIB_MINGW
+ printf(", %g TB", sz_gb / 1000);
+#else
+ printf(", %.2f TB", sz_gb / 1000);
+#endif
+ }
+ printf("\n");
+ }
+ if (st.conv_blk_num) {
+ total_sz = st.conv_blk_num * block_size;
+ sz_mb = (double)(total_sz) / (double)(1048576);
+ sz_gb = (double)(total_sz) / (double)(1000000000L);
+ printf("Size of all conventional zones: ");
+#ifdef SG_LIB_MINGW
+ printf("%" PRIu64 " bytes, %g MiB, %g GB", total_sz, sz_mb,
+ sz_gb);
+#else
+ printf("%" PRIu64 " bytes, %.1f MiB, %.2f GB", total_sz,
+ sz_mb, sz_gb);
+#endif
+ if (sz_gb > 2000) {
+#ifdef SG_LIB_MINGW
+ printf(", %g TB", sz_gb / 1000);
+#else
+ printf(", %.2f TB", sz_gb / 1000);
+#endif
+ }
+ printf("\n");
+ }
+ }
+ return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool no_final_msg = false;
+ bool as_json;
+ int res, c, act_len, rlen, in_len, off;
+ int sg_fd = -1;
+ int resid = 0;
+ int ret = 0;
+ uint32_t decod_len;
+ int64_t ll;
+ const char * device_name = NULL;
+ uint8_t * rzBuff = NULL;
+ uint8_t * free_rzbp = NULL;
+ const char * cmd_name = "Report zones";
+ sgj_state * jsp;
+ sgj_opaque_p jop = NULL;
+ char b[80];
+ struct opts_t opts SG_C_CPP_ZERO_INIT;
+ struct opts_t * op = &opts;
+
+ op->serv_act = REPORT_ZONES_SA;
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "bdefF:hHi:j::l:m:n:o:prRs:SvVw",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'b':
+ op->do_brief = true;
+ break;
+ case 'd':
+ op->do_zdomains = true;
+ op->serv_act = REPORT_ZONE_DOMAINS_SA;
+ break;
+ case 'e':
+ op->do_realms = true;
+ op->serv_act = REPORT_REALMS_SA;
+ break;
+ case 'f':
+ op->do_force = true;
+ break;
+ case 'F':
+ off = (('-' == *optarg) || ('!' == *optarg)) ? 1 : 0;
+ if (isdigit(*(optarg + off))) {
+ op->find_zt = sg_get_num_nomult(optarg + off);
+ if (op->find_zt < 0) {
+ pr2serr("bad numeric argument to '--find='\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (off)
+ op->find_zt = -op->find_zt; /* find first not equal */
+ } else { /* check for abbreviation */
+ struct zt_num2abbrev_t * zn2ap = zt_num2abbrev;
+
+ for ( ; zn2ap->abbrev; ++zn2ap) {
+ if (0 == strcmp(optarg + off, zn2ap->abbrev))
+ break;
+ }
+ if (NULL == zn2ap->abbrev) {
+ pr2serr("bad abbreviation argument to '--find='\n\n");
+ prn_zone_type_abbrevs();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->find_zt = off ? -zn2ap->ztn : zn2ap->ztn;
+ }
+ break;
+ case 'h':
+ case '?':
+ ++op->do_help;
+ break;
+ case 'H':
+ ++op->do_hex;
+ break;
+ case 'i':
+ op->in_fn = optarg;
+ break;
+ case 'j':
+ if (! sgj_init_state(&op->json_st, optarg)) {
+ int bad_char = op->json_st.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;
+ }
+ break;
+ /* case 'l': is under case 's': */
+ case 'm':
+ op->maxlen = sg_get_num(optarg);
+ if ((op->maxlen < 0) || (op->maxlen > MAX_RZONES_BUFF_LEN)) {
+ pr2serr("argument to '--maxlen' should be %d or "
+ "less\n", MAX_RZONES_BUFF_LEN);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->maxlen_given = true;
+ break;
+ case 'n':
+ op->do_num = sg_get_num(optarg);
+ if (op->do_num < 0) {
+ pr2serr("argument to '--num' should be zero or more\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'o':
+ op->reporting_opt = sg_get_num_nomult(optarg);
+ if ((op->reporting_opt < 0) || (op->reporting_opt > 63)) {
+ pr2serr("bad argument to '--report=OPT', expect 0 to "
+ "63\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'p':
+ op->do_partial = true;
+ break;
+ case 'r':
+ op->do_raw = true;
+ break;
+ case 'R':
+ op->o_readonly = true;
+ break;
+ case 's':
+ case 'l': /* --locator= and --start= are interchangeable */
+ if ((2 == strlen(optarg)) && (0 == memcmp("-1", optarg, 2))) {
+ op->st_lba = UINT64_MAX;
+ break;
+ }
+ ll = sg_get_llnum(optarg);
+ if (-1 == ll) {
+ pr2serr("bad argument to '--start=LBA' or '--locator=LBA\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->st_lba = (uint64_t)ll;
+ break;
+ case 'S':
+ op->statistics = true;
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->vb;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ case 'w':
+ op->wp_only = true;
+ break;
+ default:
+ pr2serr("unrecognised option code 0x%x ??\n", c);
+ usage(1);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == device_name) {
+ device_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+ usage(1);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+#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->vb = 0;
+ } else if (! op->verbose_given) {
+ pr2serr("set '-vv'\n");
+ op->vb = 2;
+ } else
+ pr2serr("keep verbose=%d\n", op->vb);
+#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: %s\n", version_str);
+ return 0;
+ }
+
+ if (op->do_help) {
+ usage(op->do_help);
+ return 0;
+ }
+ as_json = op->json_st.pr_as_json;
+ jsp = &op->json_st;
+ if (as_json)
+ jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
+
+ if (op->do_zdomains && op->do_realms) {
+ pr2serr("Can't have both --domain and --realm\n");
+ return SG_LIB_SYNTAX_ERROR;
+ } else if (op->do_zdomains)
+ cmd_name = "Report zone domains";
+ else if (op->do_realms)
+ cmd_name = "Report realms";
+ if (as_json)
+ sgj_js_nv_s(jsp, jop, "scsi_command_name", cmd_name);
+ if ((op->serv_act != REPORT_ZONES_SA) && op->do_partial) {
+ pr2serr("Can only use --partial with REPORT ZONES\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (device_name && op->in_fn) {
+ pr2serr("ignoring DEVICE, best to give DEVICE or --inhex=FN, but "
+ "not both\n");
+ device_name = NULL;
+ }
+ if (0 == op->maxlen)
+ op->maxlen = DEF_RZONES_BUFF_LEN;
+ rzBuff = (uint8_t *)sg_memalign(op->maxlen, 0, &free_rzbp, op->vb > 3);
+ if (NULL == rzBuff) {
+ pr2serr("unable to sg_memalign %d bytes\n", op->maxlen);
+ return sg_convert_errno(ENOMEM);
+ }
+
+ if (NULL == device_name) {
+ if (op->in_fn) {
+ if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rzBuff,
+ &in_len, op->maxlen))) {
+ if (SG_LIB_LBA_OUT_OF_RANGE == ret) {
+ no_final_msg = true;
+ pr2serr("... decode what we have, --maxlen=%d needs to "
+ "be increased\n", op->maxlen);
+ } else
+ goto the_end;
+ }
+ if (op->vb > 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->in_fn, in_len);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto the_end;
+ }
+ res = 0;
+ if (op->find_zt) { /* so '-F none' will drop through */
+ op->maxlen = in_len;
+ ret = find_report_zones(sg_fd, rzBuff, cmd_name, op, jsp);
+ goto the_end;
+ } else if (op->statistics) {
+ op->maxlen = in_len;
+ ret = gather_statistics(sg_fd, rzBuff, cmd_name, op);
+ goto the_end;
+ }
+ goto start_response;
+ } else {
+ pr2serr("missing device name!\n\n");
+ usage(1);
+ ret = SG_LIB_FILE_ERROR;
+ no_final_msg = true;
+ goto the_end;
+ }
+ }
+
+ if (op->do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ ret = SG_LIB_FILE_ERROR;
+ goto the_end;
+ }
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, op->o_readonly, op->vb);
+ if (sg_fd < 0) {
+ if (op->vb)
+ pr2serr("open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ ret = sg_convert_errno(-sg_fd);
+ goto the_end;
+ }
+
+ if (op->find_zt) { /* so '-F none' will drop through */
+ ret = find_report_zones(sg_fd, rzBuff, cmd_name, op, jsp);
+ goto the_end;
+ } else if (op->statistics) {
+ ret = gather_statistics(sg_fd, rzBuff, cmd_name, op);
+ goto the_end;
+ }
+ res = sg_ll_report_zzz(sg_fd, op->serv_act, op->st_lba, op->do_partial,
+ op->reporting_opt, rzBuff, op->maxlen, &resid,
+ true, op->vb);
+ ret = res;
+start_response:
+ if (0 == res) {
+ rlen = op->in_fn ? in_len : (op->maxlen - resid);
+ if (rlen < 4) {
+ pr2serr("Decoded response length (%d) too short\n", rlen);
+ ret = SG_LIB_CAT_MALFORMED;
+ goto the_end;
+ }
+ decod_len = sg_get_unaligned_be32(rzBuff + 0) + 64;
+ if (decod_len > WILD_RZONES_BUFF_LEN) {
+ if (! op->do_force) {
+ pr2serr("decode length [%u bytes] seems wild, use --force "
+ "override\n", decod_len);
+ ret = SG_LIB_CAT_MALFORMED;
+ goto the_end;
+ }
+ }
+ if (decod_len > (uint32_t)rlen) {
+ if ((REPORT_ZONES_SA == op->serv_act) && (! op->do_partial)) {
+ pr2serr("%u zones starting from LBA 0x%" PRIx64 " available "
+ "but only %d zones returned\n",
+ (decod_len - 64) / REPORT_ZONES_DESC_LEN, op->st_lba,
+ (rlen - 64) / REPORT_ZONES_DESC_LEN);
+ decod_len = rlen;
+ act_len = rlen;
+ } else {
+ pr2serr("decoded response length is %u bytes, but system "
+ "reports %d bytes received??\n", decod_len, rlen);
+ if (op->do_force)
+ act_len = rlen;
+ else {
+ pr2serr("Exiting, use --force to override\n");
+ ret = SG_LIB_CAT_MALFORMED;
+ goto the_end;
+ }
+ }
+ } else
+ act_len = decod_len;
+ if (op->do_raw) {
+ dStrRaw(rzBuff, act_len);
+ goto the_end;
+ }
+ if (op->do_hex && (2 != op->do_hex)) {
+ hex2stdout(rzBuff, act_len, ((1 == op->do_hex) ? 1 : -1));
+ goto the_end;
+ }
+ if (! op->wp_only && (! op->do_hex))
+ sgj_pr_hr(jsp, "%s response:\n", cmd_name);
+
+ if (act_len < 64) {
+ pr2serr("Zone length [%d] too short (perhaps after truncation\n)",
+ act_len);
+ ret = SG_LIB_CAT_MALFORMED;
+ goto the_end;
+ }
+ if (REPORT_ZONES_SA == op->serv_act)
+ ret = decode_rep_zones(rzBuff, act_len, decod_len, op, jsp);
+ else if (op->do_realms)
+ ret = decode_rep_realms(rzBuff, act_len, op, jsp);
+ else if (op->do_zdomains)
+ ret = decode_rep_zdomains(rzBuff, act_len, op, jsp);
+ } else if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr("%s command not supported\n", cmd_name);
+ else {
+ sg_get_category_sense_str(res, sizeof(b), b, op->vb);
+ pr2serr("%s command: %s\n", cmd_name, b);
+ }
+
+the_end:
+ if (free_rzbp)
+ free(free_rzbp);
+ 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->vb && (! no_final_msg))) {
+ if (! sg_if_can2stderr("sg_rep_zones 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 (as_json) {
+ if (0 == op->do_hex)
+ sgj_js2file(jsp, NULL, ret, stdout);
+ sgj_finish(jsp);
+ }
+ return ret;
+}