• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014-2022 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <limits.h>
18 #include <errno.h>
19 #include <ctype.h>
20 #include <getopt.h>
21 #define __STDC_FORMAT_MACROS 1
22 #include <inttypes.h>
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "sg_lib.h"
29 #include "sg_lib_data.h"
30 #include "sg_pt.h"
31 #include "sg_cmds_basic.h"
32 #include "sg_unaligned.h"
33 #include "sg_pr2serr.h"
34 
35 /* A utility program originally written for the Linux OS SCSI subsystem.
36  *
37  *
38  * This program issues the SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT
39  * REALMS command to the given SCSI device and decodes the response.
40  * Based on zbc2r12.pdf
41  */
42 
43 static const char * version_str = "1.42 20220807";
44 
45 #define MY_NAME "sg_rep_zones"
46 
47 #define WILD_RZONES_BUFF_LEN (1 << 28)
48 #define MAX_RZONES_BUFF_LEN (2 * 1024 * 1024)
49 #define DEF_RZONES_BUFF_LEN (1024 * 16)
50 #define RCAP16_REPLY_LEN 32
51 
52 #define SG_ZONING_IN_CMDLEN 16
53 #define REPORT_ZONES_DESC_LEN 64
54 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
55 #define DEF_PT_TIMEOUT  60      /* 60 seconds */
56 
57 /* Three zone service actions supported by this utility */
58 enum zone_report_sa_e {
59     REPORT_ZONES_SA = 0x0,
60     REPORT_REALMS_SA = 0x6,
61     REPORT_ZONE_DOMAINS_SA = 0x7
62 };
63 
64 struct opts_t {
65     bool do_brief;
66     bool do_force;
67     bool do_partial;
68     bool do_raw;
69     bool do_realms;
70     bool do_zdomains;
71     bool maxlen_given;
72     bool o_readonly;
73     bool statistics;
74     bool verbose_given;
75     bool version_given;
76     bool wp_only;
77     enum zone_report_sa_e serv_act;
78     int do_help;
79     int do_hex;
80     int do_num;
81     int find_zt;        /* negative values: find first not equal to */
82     int maxlen;
83     int reporting_opt;
84     int vb;
85     uint64_t st_lba;
86     const char * in_fn;
87     sgj_state json_st;
88 };
89 
90 struct zt_num2abbrev_t {
91     int ztn;
92     const char * abbrev;
93 };
94 
95 static struct option long_options[] = {
96         {"brief", no_argument, 0, 'b'}, /* only header and last descriptor */
97         {"domain", no_argument, 0, 'd'},
98         {"domains", no_argument, 0, 'd'},
99         {"force", no_argument, 0, 'f'},
100         {"find", required_argument, 0, 'F'},
101         {"help", no_argument, 0, 'h'},
102         {"hex", no_argument, 0, 'H'},
103         {"in", required_argument, 0, 'i'},      /* silent, same as --inhex= */
104         {"inhex", required_argument, 0, 'i'},
105         {"json", optional_argument, 0, 'j'},
106         {"locator", required_argument, 0, 'l'},
107         {"maxlen", required_argument, 0, 'm'},
108         {"num", required_argument, 0, 'n'},
109         {"partial", no_argument, 0, 'p'},
110         {"raw", no_argument, 0, 'r'},
111         {"readonly", no_argument, 0, 'R'},
112         {"realm", no_argument, 0, 'e'},
113         {"realms", no_argument, 0, 'e'},
114         {"report", required_argument, 0, 'o'},
115         {"start", required_argument, 0, 's'},
116         {"statistics", no_argument, 0, 'S'},
117         {"stats", no_argument, 0, 'S'},
118         {"verbose", no_argument, 0, 'v'},
119         {"version", no_argument, 0, 'V'},
120         {"wp", no_argument, 0, 'w'},
121         {0, 0, 0, 0},
122 };
123 
124 /* Zone types */
125 static struct zt_num2abbrev_t zt_num2abbrev[] = {
126     {0, "none"},
127     {1, "c"},           /* conventionial */
128     {2, "swr"},         /* sequential write required */
129     {3, "swp"},         /* sequential write preferred */
130     {4, "sobr"},        /* sequential or before required */
131     {5, "g"},           /* gap */
132     {-1, NULL},         /* sentinel */
133 };
134 
135 static const char * zn_dnum_s = "zone descriptor number: ";
136 
137 static const char * meaning_s = "meaning";
138 
139 
140 static void
prn_zone_type_abbrevs(void)141 prn_zone_type_abbrevs(void)
142 {
143     const struct zt_num2abbrev_t * n2ap = zt_num2abbrev;
144     char b[32];
145 
146     pr2serr("Zone type number\tAbbreviation\tName\n");
147     pr2serr("----------------\t------------\t----\n");
148     for ( ; n2ap->abbrev; ++n2ap) {
149         if (n2ap == zt_num2abbrev)
150             pr2serr("\t%d\t\t%s\t\t[reserved]\n",
151                     n2ap->ztn, n2ap->abbrev);
152         else
153             pr2serr("\t%d\t\t%s\t\t%s\n", n2ap->ztn, n2ap->abbrev,
154                     sg_get_zone_type_str(n2ap->ztn, sizeof(b), b));
155     }
156 }
157 
158 static void
usage(int h)159 usage(int h)
160 {
161     if (h > 1) goto h_twoormore;
162     pr2serr("Usage: "
163             "sg_rep_zones  [--domain] [--find=ZT] [--force] [--help] "
164             "[--hex]\n"
165             "                     [--inhex=FN] [--json[=JO]] "
166             "[--locator=LBA]\n"
167             "                     [--maxlen=LEN] [--num=NUM] [--partial] "
168             "[--raw]\n"
169             "                     [--readonly] [--realm] [--report=OPT] "
170             "[--start=LBA]\n"
171             "                     [--statistics] [--verbose] [--version] "
172             "[--wp]\n"
173             "                     DEVICE\n");
174     pr2serr("  where:\n"
175             "    --domain|-d        sends a REPORT ZONE DOMAINS command\n"
176             "    --find=ZT|-F ZT    find first zone with ZT zone type, "
177             "starting at LBA\n"
178             "                       if first character of ZT is - or !, "
179             "find first\n"
180             "                       zone that is not ZT\n"
181             "    --force|-f         bypass some sanity checks when decoding "
182             "response\n"
183             "    --help|-h          print out usage message, use twice for "
184             "more help\n"
185             "    --hex|-H           output response in hexadecimal; used "
186             "twice\n"
187             "                       shows decoded values in hex\n"
188             "    --inhex=FN|-i FN    decode contents of FN, ignore DEVICE\n"
189             "    --json[=JO]|-j[JO]    output in JSON instead of human "
190             "readable text.\n"
191             "                          Use --json=? for JSON help\n"
192             "    --locator=LBA|-l LBA    similar to --start= option\n"
193             "    --maxlen=LEN|-m LEN    max response length (allocation "
194             "length in cdb)\n"
195             "                           (def: 0 -> 8192 bytes)\n"
196             "    --num=NUM|-n NUM    number of zones to output (def: 0 -> "
197             "all)\n"
198             "    --partial|-p       sets PARTIAL bit in cdb (def: 0 -> "
199             "zone list\n"
200             "                       length not altered by allocation length "
201             "in cdb)\n"
202             "    --raw|-r           output response in binary\n"
203             "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
204             "    --realm|-e         sends a REPORT REALMS command\n"
205             "    --report=OPT|-o OP    reporting options (def: 0: all "
206             "zones)\n"
207             "    --start=LBA|-s LBA    report zones from the LBA (def: 0)\n"
208             "                          need not be a zone starting LBA\n"
209             "    --statistics|-S    gather statistics by reviewing zones\n"
210             "    --verbose|-v       increase verbosity\n"
211             "    --version|-V       print version string and exit\n"
212             "    --wp|-w            output write pointer only\n\n"
213             "Sends a SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT REALMS "
214             "command.\nBy default sends a REPORT ZONES command. Give help "
215             "option twice\n(e.g. '-hh') to see reporting options "
216             "enumerated.\n");
217     return;
218 h_twoormore:
219     pr2serr("Reporting options for REPORT ZONES:\n"
220             "    0x0    list all zones\n"
221             "    0x1    list zones with a zone condition of EMPTY\n"
222             "    0x2    list zones with a zone condition of IMPLICITLY "
223             "OPENED\n"
224             "    0x3    list zones with a zone condition of EXPLICITLY "
225             "OPENED\n"
226             "    0x4    list zones with a zone condition of CLOSED\n"
227             "    0x5    list zones with a zone condition of FULL\n"
228             "    0x6    list zones with a zone condition of READ ONLY\n"
229             "    0x7    list zones with a zone condition of OFFLINE\n"
230             "    0x8    list zones with a zone condition of INACTIVE\n"
231             "    0x10   list zones with RWP Recommended set to true\n"
232             "    0x11   list zones with Non-sequential write resources "
233             "active set to true\n"
234             "    0x3e   list zones except those with zone type: GAP\n"
235             "    0x3f   list zones with a zone condition of NOT WRITE "
236             "POINTER\n\n");
237     pr2serr("Reporting options for REPORT ZONE DOMAINS:\n"
238             "    0x0    list all zone domains\n"
239             "    0x1    list all zone domains in which all zones are active\n"
240             "    0x2    list all zone domains that contain active zones\n"
241             "    0x3    list all zone domains that do not contain any active "
242             "zones\n\n");
243     pr2serr("Reporting options for REPORT REALMS:\n"
244             "    0x0    list all realms\n"
245             "    0x1    list all realms that contain active Sequential Or "
246             "Before Required zones\n"
247             "    0x2    list all realms that contain active Sequential Write "
248             "Required zones\n"
249             "    0x3    list all realms that contain active Sequential Write "
250             "Preferred zones\n");
251     pr2serr("\n");
252     prn_zone_type_abbrevs();
253 }
254 
255 /* Invokes a SCSI REPORT ZONES, REPORT ZONE DOMAINS or REPORT REALMS command
256  * (see ZBC and ZBC-2).  Return of 0 -> success, various SG_LIB_CAT_* positive
257  * values or -1 -> other errors */
258 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)259 sg_ll_report_zzz(int sg_fd, enum zone_report_sa_e serv_act, uint64_t zs_lba,
260                  bool partial, int report_opts, void * resp, int mx_resp_len,
261                  int * residp, bool noisy, int vb)
262 {
263     int ret, res, sense_cat;
264     uint8_t rz_cdb[SG_ZONING_IN_CMDLEN] =
265           {SG_ZONING_IN, REPORT_ZONES_SA, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
266            0, 0, 0, 0};
267     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
268     struct sg_pt_base * ptvp;
269 
270     rz_cdb[1] = (uint8_t)serv_act;
271     sg_put_unaligned_be64(zs_lba, rz_cdb + 2);
272     sg_put_unaligned_be32((uint32_t)mx_resp_len, rz_cdb + 10);
273     rz_cdb[14] = report_opts & 0x3f;
274     if (partial)
275         rz_cdb[14] |= 0x80;
276     if (vb) {
277         char b[128];
278 
279         pr2serr("    %s\n", sg_get_command_str(rz_cdb, SG_ZONING_IN_CMDLEN,
280                                                true, sizeof(b), b));
281     }
282     ptvp = construct_scsi_pt_obj();
283     if (NULL == ptvp) {
284         pr2serr("%s: out of memory\n", __func__);
285         return -1;
286     }
287     set_scsi_pt_cdb(ptvp, rz_cdb, sizeof(rz_cdb));
288     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
289     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
290     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, vb);
291     ret = sg_cmds_process_resp(ptvp, "report zone/domain/realm", res, noisy,
292                                vb, &sense_cat);
293     if (-1 == ret) {
294         if (get_scsi_pt_transport_err(ptvp))
295             ret = SG_LIB_TRANSPORT_ERROR;
296         else
297             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
298     } else if (-2 == ret) {
299         switch (sense_cat) {
300         case SG_LIB_CAT_RECOVERED:
301         case SG_LIB_CAT_NO_SENSE:
302             ret = 0;
303             break;
304         default:
305             ret = sense_cat;
306             break;
307         }
308     } else
309         ret = 0;
310     if (residp)
311         *residp = get_scsi_pt_resid(ptvp);
312     destruct_scsi_pt_obj(ptvp);
313     return ret;
314 }
315 
316 static void
dStrRaw(const uint8_t * str,int len)317 dStrRaw(const uint8_t * str, int len)
318 {
319     int k;
320 
321     for (k = 0; k < len; ++k)
322         printf("%c", str[k]);
323 }
324 
325 static const char *
zone_condition_str(int zc,char * b,int blen,int vb)326 zone_condition_str(int zc, char * b, int blen, int vb)
327 {
328     const char * cp;
329 
330     if (NULL == b)
331         return "zone_condition_str: NULL ptr)";
332     switch (zc) {
333     case 0:
334         cp = "Not write pointer";
335         break;
336     case 1:
337         cp = "Empty";
338         break;
339     case 2:
340         cp = "Implicitly opened";
341         break;
342     case 3:
343         cp = "Explicitly opened";
344         break;
345     case 4:
346         cp = "Closed";
347         break;
348     case 5:
349         cp = "Inactive";
350         break;
351     case 0xd:
352         cp = "Read only";
353         break;
354     case 0xe:
355         cp = "Full";
356         break;
357     case 0xf:
358         cp = "Offline";
359         break;
360     default:
361         cp = NULL;
362         break;
363     }
364     if (cp) {
365         if (vb)
366             snprintf(b, blen, "%s [0x%x]", cp, zc);
367         else
368             snprintf(b, blen, "%s", cp);
369     } else
370         snprintf(b, blen, "Reserved [0x%x]", zc);
371     return b;
372 }
373 
374 static const char * same_desc_arr[16] = {
375     "zone type and length may differ in each descriptor",
376     "zone type and length same in each descriptor",
377     "zone type and length same apart from length in last descriptor",
378     "zone type for each descriptor may be different",
379     "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
380     "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
381     "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
382 };
383 
384 static uint64_t
prt_a_zn_desc(const uint8_t * bp,const struct opts_t * op,sgj_state * jsp,sgj_opaque_p jop)385 prt_a_zn_desc(const uint8_t *bp, const struct opts_t * op,
386               sgj_state * jsp, sgj_opaque_p jop)
387 {
388     uint8_t zt, zc;
389     uint64_t lba, len, wp;
390     char b[80];
391 
392     jop = jop ? jop : jsp->basep;
393     zt = bp[0] & 0xf;
394     zc = (bp[1] >> 4) & 0xf;
395     sg_get_zone_type_str(zt, sizeof(b), b);
396     sgj_pr_hr(jsp, "   Zone type: %s\n", b);
397     sgj_js_nv_istr(jsp, jop, "zone_type", zt, meaning_s, b);
398     zone_condition_str(zc, b, sizeof(b), op->vb);
399     sgj_pr_hr(jsp, "   Zone condition: %s\n", b);
400     sgj_js_nv_istr(jsp, jop, "zone_condition", zc, meaning_s, b);
401     sgj_haj_vi(jsp, jop, 3, "PUEP", SGJ_SEP_COLON_1_SPACE,
402                !!(bp[1] & 0x4), false);
403     sgj_haj_vi(jsp, jop, 3, "NON_SEQ", SGJ_SEP_COLON_1_SPACE,
404                !!(bp[1] & 0x2), false);
405     sgj_haj_vi(jsp, jop, 3, "RESET", SGJ_SEP_COLON_1_SPACE,
406                !!(bp[1] & 0x1), false);
407     len = sg_get_unaligned_be64(bp + 8);
408     sgj_pr_hr(jsp, "   Zone Length: 0x%" PRIx64 "\n", len);
409     sgj_js_nv_ihex(jsp, jop, "zone_length", (int64_t)len);
410     lba = sg_get_unaligned_be64(bp + 16);
411     sgj_pr_hr(jsp, "   Zone start LBA: 0x%" PRIx64 "\n", lba);
412     sgj_js_nv_ihex(jsp, jop, "zone_start_lba", (int64_t)lba);
413     wp = sg_get_unaligned_be64(bp + 24);
414     if (sg_all_ffs((const uint8_t *)&wp, sizeof(wp)))
415         sgj_pr_hr(jsp, "   Write pointer LBA: -1\n");
416     else
417         sgj_pr_hr(jsp, "   Write pointer LBA: 0x%" PRIx64 "\n", wp);
418     sgj_js_nv_ihex(jsp, jop, "write_pointer_lba", (int64_t)wp);
419     return lba + len;
420 }
421 
422 static int
decode_rep_zones(const uint8_t * rzBuff,int act_len,uint32_t decod_len,const struct opts_t * op,sgj_state * jsp)423 decode_rep_zones(const uint8_t * rzBuff, int act_len, uint32_t decod_len,
424                  const struct opts_t * op, sgj_state * jsp)
425 {
426     bool as_json = jsp ? jsp->pr_as_json : false;
427     int k, same, num_zd;
428     uint64_t wp, ul, mx_lba;
429     sgj_opaque_p jop = jsp ? jsp->basep : NULL;
430     sgj_opaque_p jap = NULL;
431     const uint8_t * bp;
432 
433     if ((uint32_t)act_len < decod_len) {
434         num_zd = (act_len >= 64) ? ((act_len - 64) / REPORT_ZONES_DESC_LEN)
435                                  : 0;
436         if (act_len == op->maxlen) {
437             if (op->maxlen_given)
438                 pr2serr("decode length [%u bytes] may be constrained by "
439                         "given --maxlen value, try increasing\n", decod_len);
440             else
441                 pr2serr("perhaps --maxlen=%u needs to be used\n", decod_len);
442         } else if (op->in_fn)
443             pr2serr("perhaps %s has been truncated\n", op->in_fn);
444     } else
445         num_zd = (decod_len - 64) / REPORT_ZONES_DESC_LEN;
446     same = rzBuff[4] & 0xf;
447     mx_lba = sg_get_unaligned_be64(rzBuff + 8);
448     if (op->wp_only) {
449         ;
450     } else if (op->do_hex) {
451         hex2stdout(rzBuff, 64, -1);
452         printf("\n");
453     } else {
454         uint64_t rzslbag = sg_get_unaligned_be64(rzBuff + 16);
455         static const char * rzslbag_s = "Reported zone starting LBA "
456                                         "granularity";
457 
458         sgj_pr_hr(jsp, "  Same=%d: %s\n", same, same_desc_arr[same]);
459         sgj_js_nv_istr(jsp, jop, "same", same, meaning_s,
460                        same_desc_arr[same]);
461         sgj_pr_hr(jsp, "  Maximum LBA: 0x%" PRIx64 "\n\n", mx_lba);
462         sgj_js_nv_ihex(jsp, jop, "maximum_lba", mx_lba);
463         sgj_pr_hr(jsp, "  %s: 0x%" PRIx64 "\n\n", rzslbag_s, rzslbag);
464         sgj_js_nv_ihex(jsp, jop, rzslbag_s, rzslbag);
465     }
466     if (op->do_num > 0)
467             num_zd = (num_zd > op->do_num) ? op->do_num : num_zd;
468     if (((uint32_t)act_len < decod_len) &&
469         ((num_zd * REPORT_ZONES_DESC_LEN) + 64 > act_len)) {
470         pr2serr("Skip due to truncated response, try using --num= to a "
471                 "value less than %d\n", num_zd);
472         return SG_LIB_CAT_MALFORMED;
473     }
474     if (op->do_brief && (num_zd > 0)) {
475         bp = rzBuff + 64 + ((num_zd - 1) * REPORT_ZONES_DESC_LEN);
476         if (op->do_hex) {
477             if (op->wp_only)
478                 hex2stdout(bp + 24, 8, -1);
479             else
480                 hex2stdout(bp, 64, -1);
481             return 0;
482         }
483         sgj_pr_hr(jsp, "From last descriptor in this response:\n");
484         sgj_pr_hr(jsp, " %s%d\n", zn_dnum_s, num_zd - 1);
485         sgj_js_nv_i(jsp, jop, "zone_descriptor_index", num_zd - 1);
486         ul = prt_a_zn_desc(bp, op, jsp, jop);
487         if (ul > mx_lba)
488             sgj_pr_hr(jsp, "   >> This zone seems to be the last one\n");
489         else
490             sgj_pr_hr(jsp, "   >> Probable next Zone start LBA: 0x%" PRIx64
491                       "\n", ul);
492         return 0;
493     }
494     if (as_json)
495         jap = sgj_named_subarray_r(jsp, NULL, "zone_descriptors_list");
496     for (k = 0, bp = rzBuff + 64; k < num_zd;
497          ++k, bp += REPORT_ZONES_DESC_LEN) {
498         sgj_opaque_p jo2p;
499 
500         if (! op->wp_only)
501              sgj_pr_hr(jsp, " %s%d\n", zn_dnum_s, k);
502         if (op->do_hex) {
503             hex2stdout(bp, 64, -1);
504             continue;
505         }
506         if (op->wp_only) {
507             if (op->do_hex)
508                 hex2stdout(bp + 24, 8, -1);
509             else {
510                 wp = sg_get_unaligned_be64(bp + 24);
511                 if (sg_all_ffs((const uint8_t *)&wp, sizeof(wp)))
512                     sgj_pr_hr(jsp, "-1\n");
513                 else
514                     sgj_pr_hr(jsp, "0x%" PRIx64 "\n", wp);
515                 jo2p = sgj_new_unattached_object_r(jsp);
516                 sgj_js_nv_ihex(jsp, jo2p, "write_pointer_lba", (int64_t)wp);
517                 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
518             }
519             continue;
520         }
521         jo2p = sgj_new_unattached_object_r(jsp);
522         prt_a_zn_desc(bp, op, jsp, jo2p);
523         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
524     }
525     if ((op->do_num == 0) && (! op->wp_only) && (! op->do_hex)) {
526         if ((64 + (REPORT_ZONES_DESC_LEN * (uint32_t)num_zd)) < decod_len)
527             sgj_pr_hr(jsp, "\n>>> Beware: Zone list truncated, may need "
528                       "another call\n");
529     }
530     return 0;
531 }
532 
533 static int
decode_rep_realms(const uint8_t * rzBuff,int act_len,const struct opts_t * op,sgj_state * jsp)534 decode_rep_realms(const uint8_t * rzBuff, int act_len,
535                   const struct opts_t * op, sgj_state * jsp)
536 {
537     uint32_t k, realms_count, derived_realms_count, r_desc_len,
538              zdomains_count;
539     uint64_t nr_locator;
540     const uint8_t * bp;
541     sgj_opaque_p jop = jsp ? jsp->basep : NULL;
542     sgj_opaque_p jap = NULL;
543     sgj_opaque_p ja2p = NULL;
544 
545     if (act_len < 12) {
546         pr2serr("need more than 12 bytes to decode, got %u\n", act_len);
547         return SG_LIB_CAT_MALFORMED;
548     }
549     realms_count = sg_get_unaligned_be32(rzBuff + 4);
550     r_desc_len = sg_get_unaligned_be32(rzBuff + 8);
551     if (act_len < 20)
552         nr_locator = sg_get_unaligned_be64(rzBuff + 12);
553     else
554         nr_locator = 0;
555     sgj_haj_vi(jsp, jop, 0, "Realms_count", SGJ_SEP_EQUAL_NO_SPACE,
556                realms_count, true);
557     sgj_haj_vi(jsp, jop, 0, "Realms_descriptor_length",
558                SGJ_SEP_EQUAL_NO_SPACE, r_desc_len, true);
559     sgj_pr_hr(jsp, "Next_realm_locator=0x%" PRIx64 "\n", nr_locator);
560     sgj_js_nv_ihex(jsp, jop, "Next_realm_locator", nr_locator);
561     if ((realms_count < 1) || (act_len < (64 + 16)) || (r_desc_len < 16)) {
562         if (op->vb) {
563             pr2serr("%s: exiting early because ", __func__);
564             if (realms_count < 1)
565                 pr2serr("realms_count is zero\n");
566             else if (r_desc_len < 16)
567                 pr2serr("realms descriptor length less than 16\n");
568             else
569                 pr2serr("actual_length (%u) too short\n", act_len);
570         }
571         return 0;
572     }
573     derived_realms_count = (act_len - 64) / r_desc_len;
574     if (derived_realms_count > realms_count) {
575         if (op->vb)
576             pr2serr("%s: derived_realms_count [%u] > realms_count [%u]\n",
577                     __func__, derived_realms_count, realms_count);
578     } else if (derived_realms_count < realms_count) {
579         if (op->vb)
580             pr2serr("%s: derived_realms_count [%u] < realms_count [%u], "
581                     "use former\n", __func__, derived_realms_count,
582                     realms_count);
583         realms_count = derived_realms_count;
584     }
585     zdomains_count = (r_desc_len - 16) / 16;
586 
587     if (op->do_num > 0)
588             realms_count = (realms_count > (uint32_t)op->do_num) ?
589                                 (uint32_t)op->do_num : realms_count;
590     jap = sgj_named_subarray_r(jsp, jop, "realm_descriptors_list");
591 
592     for (k = 0, bp = rzBuff + 64; k < realms_count; ++k, bp += r_desc_len) {
593         uint32_t j;
594         uint16_t restrictions;
595         const uint8_t * zp;
596         sgj_opaque_p jo2p;
597 
598         jo2p = sgj_new_unattached_object_r(jsp);
599         sgj_haj_vi(jsp, jo2p, 1, "Realms_id", SGJ_SEP_EQUAL_NO_SPACE,
600                    sg_get_unaligned_be32(bp + 0), true);
601         if (op->do_hex) {
602             hex2stdout(bp, r_desc_len, -1);
603             continue;
604         }
605         restrictions = sg_get_unaligned_be16(bp + 4);
606         sgj_pr_hr(jsp, "   realm_restrictions=0x%hu\n", restrictions);
607         sgj_js_nv_ihex(jsp, jo2p, "realm_restrictions", restrictions);
608         sgj_haj_vi(jsp, jo2p, 3, "active_zone_domain_id",
609                    SGJ_SEP_EQUAL_NO_SPACE, bp[7], true);
610 
611         ja2p = sgj_named_subarray_r(jsp, jo2p,
612                                     "realm_start_end_descriptors_list");
613         for (j = 0, zp = bp + 16; j < zdomains_count; ++j, zp += 16) {
614             uint64_t lba;
615             sgj_opaque_p jo3p;
616 
617             jo3p = sgj_new_unattached_object_r(jsp);
618             sgj_pr_hr(jsp, "   zone_domain=%u\n", j);
619             sgj_js_nv_i(jsp, jo3p, "corresponding_zone_domain_id", j);
620             lba = sg_get_unaligned_be64(zp + 0);
621             sgj_pr_hr(jsp, "     starting_lba=0x%" PRIx64 "\n", lba);
622             sgj_js_nv_ihex(jsp, jo3p, "realm_starting_lba", (int64_t)lba);
623             lba = sg_get_unaligned_be64(zp + 8);
624             sgj_pr_hr(jsp, "     ending_lba=0x%" PRIx64 "\n", lba);
625             sgj_js_nv_ihex(jsp, jo3p, "realm_ending_lba", (int64_t)lba);
626             sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
627         }
628         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
629     }
630     return 0;
631 }
632 
633 static int
decode_rep_zdomains(const uint8_t * rzBuff,int act_len,const struct opts_t * op,sgj_state * jsp)634 decode_rep_zdomains(const uint8_t * rzBuff, int act_len,
635                    const struct opts_t * op, sgj_state * jsp)
636 {
637     uint32_t k, zd_len, zd_ret_len, zdoms_sup, zdoms_rep, zd_rep_opts;
638     uint32_t num, der_zdoms;
639     uint64_t zd_locator;
640     sgj_opaque_p jop = jsp ? jsp->basep : NULL;
641     sgj_opaque_p jap = NULL;
642     const uint8_t * bp;
643 
644     if (act_len < 12) {
645         pr2serr("need more than 12 bytes to decode, got %u\n", act_len);
646         return SG_LIB_CAT_MALFORMED;
647     }
648     zd_len = sg_get_unaligned_be32(rzBuff + 0);
649     zd_ret_len = sg_get_unaligned_be32(rzBuff + 4);
650     zdoms_sup = rzBuff[8];
651     zdoms_rep = rzBuff[9];
652     zd_rep_opts = rzBuff[10];
653     if (act_len < 24)
654         zd_locator = sg_get_unaligned_be64(rzBuff + 16);
655     else
656         zd_locator = 0;
657     sgj_haj_vi(jsp, jop, 0, "Zone_domains_returned_list_length=",
658                SGJ_SEP_EQUAL_NO_SPACE, zd_ret_len, true);
659     sgj_haj_vi(jsp, jop, 0, "Zone_domains_supported",
660                SGJ_SEP_EQUAL_NO_SPACE, zdoms_sup, true);
661     sgj_haj_vi(jsp, jop, 0, "Zone_domains_reported",
662                SGJ_SEP_EQUAL_NO_SPACE, zdoms_rep, true);
663     sgj_pr_hr(jsp, "Reporting_options=0x%x\n", zd_rep_opts);
664     sgj_js_nv_ihex(jsp, jop, "Reporting_options", zd_rep_opts);
665     sgj_pr_hr(jsp, "Zone_domain_locator=0x%" PRIx64 "\n", zd_locator);
666     sgj_js_nv_ihex(jsp, jop, "Zone_domain_locator", zd_locator);
667 
668     der_zdoms = zd_len / 96;
669     if (op->vb > 1)
670         pr2serr("Derived zdomains=%u\n", der_zdoms);
671     num = ((der_zdoms < zdoms_rep) ? der_zdoms : zdoms_rep) * 96;
672     jap = sgj_named_subarray_r(jsp, jop, "zone_domain_descriptors_list");
673 
674     for (k = 0, bp = rzBuff + 64; k < num; k += 96, bp += 96) {
675         uint64_t lba;
676         sgj_opaque_p jo2p;
677 
678         jo2p = sgj_new_unattached_object_r(jsp);
679         sgj_haj_vi(jsp, jo2p, 3, "zone_domain",
680                    SGJ_SEP_EQUAL_NO_SPACE, bp[0], true);
681         lba = sg_get_unaligned_be64(bp + 16);
682         sgj_pr_hr(jsp, "     zone_count=%" PRIu64 "\n", lba);
683         sgj_js_nv_ihex(jsp, jo2p, "zone_count", lba);
684         lba = sg_get_unaligned_be64(bp + 24);
685         sgj_pr_hr(jsp, "     starting_lba=0x%" PRIx64 "\n", lba);
686         sgj_js_nv_ihex(jsp, jo2p, "starting_lba", lba);
687         lba = sg_get_unaligned_be64(bp + 32);
688         sgj_pr_hr(jsp, "     ending_lba=0x%" PRIx64 "\n", lba);
689         sgj_js_nv_ihex(jsp, jo2p, "ending_lba", lba);
690         sgj_pr_hr(jsp, "     zone_domain_zone_type=0x%x\n", bp[40]);
691         sgj_js_nv_ihex(jsp, jo2p, "zone_domain_zone_type", bp[40]);
692         sgj_haj_vi(jsp, jo2p, 5, "VZDZT", SGJ_SEP_EQUAL_NO_SPACE,
693                    !!(0x2 & bp[42]), false);
694         sgj_haj_vi(jsp, jo2p, 5, "SRB", SGJ_SEP_EQUAL_NO_SPACE,
695                    !!(0x1 & bp[42]), false);
696         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
697     }
698     return 0;
699 }
700 
701 static int
find_report_zones(int sg_fd,uint8_t * rzBuff,const char * cmd_name,struct opts_t * op,sgj_state * jsp)702 find_report_zones(int sg_fd, uint8_t * rzBuff, const char * cmd_name,
703                   struct opts_t * op, sgj_state * jsp)
704 {
705     bool as_json = (jsp && (0 == op->do_hex)) ?  jsp->pr_as_json : false;
706     bool found = false;
707     uint8_t zt;
708     int k, res, resid, rlen, num_zd, num_rem;
709     uint32_t zn_dnum = 0;
710     uint64_t slba = op->st_lba;
711     uint64_t mx_lba = 0;
712     const uint8_t * bp = rzBuff;
713     char b[96];
714 
715     num_rem = op->do_num ? op->do_num : INT_MAX;
716     for ( ; num_rem > 0; num_rem -= num_zd) {
717         resid = 0;
718         if (sg_fd >= 0) {
719             res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba,
720                                    true /* set partial */, op->reporting_opt,
721                                    rzBuff, op->maxlen, &resid, true, op->vb);
722             if (res) {
723                 if (SG_LIB_CAT_INVALID_OP == res)
724                     pr2serr("%s: %s%u, %s command not supported\n", __func__,
725                             zn_dnum_s, zn_dnum, cmd_name);
726                 else {
727                     sg_get_category_sense_str(res, sizeof(b), b, op->vb);
728                     pr2serr("%s: %s%u, %s command: %s\n", __func__,
729                             zn_dnum_s, zn_dnum, cmd_name, b);
730                 }
731                 break;
732             }
733         } else
734             res = 0;
735         rlen = op->maxlen - resid;
736         if (rlen <= 64)
737             break;
738         mx_lba = sg_get_unaligned_be64(rzBuff + 8);
739         num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN;
740         if (num_zd > num_rem)
741             num_zd = num_rem;
742         for (k = 0, bp = rzBuff + 64; k < num_zd;
743              ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) {
744             zt = 0xf & bp[0];
745             if (op->find_zt > 0) {
746                 if ((uint8_t)op->find_zt == zt )
747                     break;
748             } else if (op->find_zt < 0) {
749                 if ((uint8_t)(-op->find_zt) != zt )
750                     break;
751             }
752             slba = sg_get_unaligned_be64(bp + 16) +
753                    sg_get_unaligned_be64(bp + 8);
754         }
755         if (k < num_zd) {
756             found = true;
757             break;
758         } else if ((slba > mx_lba) || (sg_fd < 0))
759             break;
760     }           /* end of outer for loop */
761     if (res == 0) {
762         sgj_opaque_p jo2p = as_json ?
763                 sgj_named_subobject_r(jsp, NULL, "find_condition") : NULL;
764 
765         if (found) {
766             if (op->do_hex) {
767                 hex2stdout(rzBuff, 64, -1);
768                 printf("\n");
769                 hex2stdout(bp, 64, -1);
770             } else {
771                 sgj_pr_hr(jsp, "Condition met at:\n");
772                 sgj_pr_hr(jsp, " %s: %d\n", zn_dnum_s, zn_dnum);
773                 sgj_js_nv_b(jsp, jo2p, "met", true);
774                 sgj_js_nv_i(jsp, jo2p, "zone_descriptor_index", zn_dnum);
775                 prt_a_zn_desc(bp, op, jsp, jo2p);
776             }
777         } else {
778             if (op->do_hex) {
779                 memset(b, 0xff, 64);
780                 hex2stdout((const uint8_t *)b, 64, -1);
781             } else {
782                 sgj_js_nv_b(jsp, jo2p, "met", false);
783                 sgj_js_nv_i(jsp, jo2p, "zone_descriptor_index", zn_dnum);
784                 if (num_rem < 1)
785                     sgj_pr_hr(jsp, "Condition NOT met, checked %d zones; "
786                               "next %s%u\n", op->do_num, zn_dnum_s, zn_dnum);
787                 else
788                     sgj_pr_hr(jsp, "Condition NOT met; next %s%u\n",
789                               zn_dnum_s, zn_dnum);
790             }
791         }
792     }
793     return res;
794 }
795 
796 struct statistics_t {
797     uint32_t zt_conv_num;
798     uint32_t zt_swr_num;
799     uint32_t zt_swp_num;
800     uint32_t zt_sob_num;
801     uint32_t zt_gap_num;
802     uint32_t zt_unk_num;
803 
804     uint32_t zc_nwp_num;
805     uint32_t zc_mt_num;
806     uint32_t zc_iop_num;
807     uint32_t zc_eop_num;
808     uint32_t zc_cl_num;
809     uint32_t zc_ina_num;
810     uint32_t zc_ro_num;
811     uint32_t zc_full_num;
812     uint32_t zc_off_num;
813     uint32_t zc_unk_num;
814 
815     /* The following LBAs have 1 added to them, initialized to 0 */
816     uint64_t zt_swr_1st_lba1;
817     uint64_t zt_swp_1st_lba1;
818     uint64_t zt_sob_1st_lba1;
819     uint64_t zt_gap_1st_lba1;
820 
821     uint64_t zc_nwp_1st_lba1;
822     uint64_t zc_mt_1st_lba1;
823     uint64_t zc_iop_1st_lba1;
824     uint64_t zc_eop_1st_lba1;
825     uint64_t zc_cl_1st_lba1;
826     uint64_t zc_ina_1st_lba1;
827     uint64_t zc_ro_1st_lba1;
828     uint64_t zc_full_1st_lba1;
829     uint64_t zc_off_1st_lba1;
830 
831     uint64_t wp_max_lba1;       /* ... that isn't Zone start LBA */
832     uint64_t wp_blk_num;        /* sum of (zwp - zs_lba) */
833     uint64_t conv_blk_num;      /* sum of (z_blks) of zt=conv */
834 };
835 
836 static int
gather_statistics(int sg_fd,uint8_t * rzBuff,const char * cmd_name,struct opts_t * op)837 gather_statistics(int sg_fd, uint8_t * rzBuff, const char * cmd_name,
838                   struct opts_t * op)
839 {
840     uint8_t zt, zc;
841     int k, res, resid, rlen, num_zd, num_rem;
842     uint32_t zn_dnum = 0;
843     uint64_t slba = op->st_lba;
844     uint64_t mx_lba = 0;
845     uint64_t zs_lba, zwp, z_blks;
846     const uint8_t * bp = rzBuff;
847     struct statistics_t st SG_C_CPP_ZERO_INIT;
848     char b[96];
849 
850     if (op->serv_act != REPORT_ZONES_SA) {
851         pr2serr("%s: do not support statistics for %s yet\n", __func__,
852                 cmd_name);
853         return SG_LIB_SYNTAX_ERROR;
854     }
855 
856     num_rem = op->do_num ? op->do_num : INT_MAX;
857     for ( ; num_rem > 0; num_rem -= num_zd) {
858         resid = 0;
859         zs_lba = slba;
860         if (sg_fd >= 0) {
861             res = sg_ll_report_zzz(sg_fd, REPORT_ZONES_SA, slba,
862                                    true /* set partial */, op->reporting_opt,
863                                    rzBuff, op->maxlen, &resid, true, op->vb);
864             if (res) {
865                 if (SG_LIB_CAT_INVALID_OP == res)
866                     pr2serr("%s: %s%u, %s command not supported\n", __func__,
867                             zn_dnum_s, zn_dnum, cmd_name);
868                 else {
869                     sg_get_category_sense_str(res, sizeof(b), b, op->vb);
870                     pr2serr("%s: %s%u, %s command: %s\n", __func__,
871                             zn_dnum_s, zn_dnum, cmd_name, b);
872                 }
873                 break;
874             }
875         } else
876             res = 0;
877         rlen = op->maxlen - resid;
878         if (rlen <= 64) {
879             break;
880         }
881         mx_lba = sg_get_unaligned_be64(rzBuff + 8);
882         num_zd = (rlen - 64) / REPORT_ZONES_DESC_LEN;
883         if (num_zd > num_rem)
884             num_zd = num_rem;
885         for (k = 0, bp = rzBuff + 64; k < num_zd;
886              ++k, bp += REPORT_ZONES_DESC_LEN, ++zn_dnum) {
887             z_blks = sg_get_unaligned_be64(bp + 8);
888             zs_lba = sg_get_unaligned_be64(bp + 16);
889             zwp = sg_get_unaligned_be64(bp + 24);
890             zt = 0xf & bp[0];
891             switch (zt) {
892             case 1:     /* conventional */
893                 ++st.zt_conv_num;
894                 st.conv_blk_num += z_blks;
895                 break;
896             case 2:     /* sequential write required */
897                 ++st.zt_swr_num;
898                 if (0 == st.zt_swr_1st_lba1)
899                     st.zt_swr_1st_lba1 = zs_lba + 1;
900                 break;
901             case 3:     /* sequential write preferred */
902                 ++st.zt_swp_num;
903                 if (0 == st.zt_swp_1st_lba1)
904                     st.zt_swp_1st_lba1 = zs_lba + 1;
905                 break;
906             case 4:     /* sequential or before (write) */
907                 ++st.zt_sob_num;
908                 if (0 == st.zt_sob_1st_lba1)
909                     st.zt_sob_1st_lba1 = zs_lba + 1;
910                 break;
911             case 5:     /* gap */
912                 ++st.zt_gap_num;
913                 if (0 == st.zt_gap_1st_lba1)
914                     st.zt_gap_1st_lba1 = zs_lba + 1;
915                 break;
916             default:
917                 ++st.zt_unk_num;
918                 break;
919             }
920             zc = (bp[1] >> 4) & 0xf;
921             switch (zc) {
922             case 0:     /* not write pointer (zone) */
923                 ++st.zc_nwp_num;
924                 if (0 == st.zc_nwp_1st_lba1)
925                     st.zc_nwp_1st_lba1 = zs_lba + 1;
926                 break;
927             case 1:     /* empty */
928                 ++st.zc_mt_num;
929                 if (0 == st.zc_mt_1st_lba1)
930                     st.zc_mt_1st_lba1 = zs_lba + 1;
931                 break;
932             case 2:     /* implicitly opened */
933                 ++st.zc_iop_num;
934                 if (0 == st.zc_iop_1st_lba1)
935                     st.zc_iop_1st_lba1 = zs_lba + 1;
936                 if (zwp > zs_lba) {
937                     st.wp_max_lba1 = zwp + 1;
938                     st.wp_blk_num += zwp - zs_lba;
939                 }
940                 break;
941             case 3:     /* explicitly opened */
942                 ++st.zc_eop_num;
943                 if (0 == st.zc_eop_1st_lba1)
944                     st.zc_eop_1st_lba1 = zs_lba + 1;
945                 if (zwp > zs_lba) {
946                     st.wp_max_lba1 = zwp + 1;
947                     st.wp_blk_num += zwp - zs_lba;
948                 }
949                 break;
950             case 4:     /* closed */
951                 ++st.zc_cl_num;
952                 if (0 == st.zc_cl_1st_lba1)
953                     st.zc_cl_1st_lba1 = zs_lba + 1;
954                 if (zwp > zs_lba) {
955                     st.wp_max_lba1 = zwp + 1;
956                     st.wp_blk_num += zwp - zs_lba;
957                 }
958                 break;
959             case 5:     /* inactive */
960                 ++st.zc_ina_num;
961                 if (0 == st.zc_ina_1st_lba1)
962                     st.zc_ina_1st_lba1 = zs_lba + 1;
963                 break;
964             case 0xd:   /* read-only */
965                 ++st.zc_ro_num;
966                 if (0 == st.zc_ro_1st_lba1)
967                     st.zc_ro_1st_lba1 = zs_lba + 1;
968                 break;
969             case 0xe:   /* full */
970                 ++st.zc_full_num;
971                 if (0 == st.zc_full_1st_lba1)
972                     st.zc_full_1st_lba1 = zs_lba + 1;
973                 st.wp_blk_num += z_blks;
974                 break;
975             case 0xf:   /* offline */
976                 ++st.zc_off_num;
977                 if (0 == st.zc_off_1st_lba1)
978                     st.zc_off_1st_lba1 = zs_lba + 1;
979                 break;
980             default:
981                 ++st.zc_unk_num;
982                 break;
983             }
984             slba = zs_lba + z_blks;
985         }       /* end of inner for loop */
986         if ((slba > mx_lba) || (sg_fd < 0))
987             break;
988     }           /* end of outer for loop */
989     printf("Number of conventional type zones: %u\n", st.zt_conv_num);
990     if (st.zt_swr_num > 0)
991         printf("Number of sequential write required type zones: %u\n",
992                st.zt_swr_num);
993     if (st.zt_swr_1st_lba1 > 0)
994         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
995               st.zt_swr_1st_lba1 - 1);
996     if (st.zt_swp_num > 0)
997         printf("Number of sequential write preferred type zones: %u\n",
998                st.zt_swp_num);
999     if (st.zt_swp_1st_lba1 > 0)
1000         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1001               st.zt_swp_1st_lba1 - 1);
1002     if (st.zt_sob_num > 0)
1003         printf("Number of sequential or before type zones: %u\n",
1004                st.zt_sob_num);
1005     if (st.zt_sob_1st_lba1 > 0)
1006         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1007               st.zt_sob_1st_lba1 - 1);
1008     if (st.zt_gap_num > 0)
1009         printf("Number of gap type zones: %u\n", st.zt_gap_num);
1010     if (st.zt_gap_1st_lba1 > 0)
1011         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1012               st.zt_gap_1st_lba1 - 1);
1013     if (st.zt_unk_num > 0)
1014         printf("Number of unknown type zones: %u\n", st.zt_unk_num);
1015 
1016     printf("Number of 'not write pointer' condition zones: %u\n",
1017            st.zc_nwp_num);
1018     if (st.zc_nwp_1st_lba1 > 0)
1019         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1020               st.zc_nwp_1st_lba1 - 1);
1021     printf("Number of empty condition zones: %u\n", st.zc_mt_num);
1022     if (st.zc_mt_1st_lba1 > 0)
1023         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1024               st.zc_mt_1st_lba1 - 1);
1025     if (st.zc_iop_num > 0)
1026         printf("Number of implicitly open condition zones: %u\n",
1027                st.zc_iop_num);
1028     if (st.zc_iop_1st_lba1 > 0)
1029         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1030               st.zc_iop_1st_lba1 - 1);
1031     if (st.zc_eop_num)
1032         printf("Number of explicitly open condition zones: %u\n",
1033                st.zc_eop_num);
1034     if (st.zc_eop_1st_lba1 > 0)
1035         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1036               st.zc_eop_1st_lba1 - 1);
1037     if (st.zc_cl_num)
1038         printf("Number of closed condition zones: %u\n", st.zc_cl_num);
1039     if (st.zc_cl_1st_lba1 > 0)
1040         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1041               st.zc_cl_1st_lba1 - 1);
1042     if (st.zc_ina_num)
1043         printf("Number of inactive condition zones: %u\n", st.zc_ina_num);
1044     if (st.zc_ina_1st_lba1 > 0)
1045         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1046               st.zc_ina_1st_lba1 - 1);
1047     if (st.zc_ro_num)
1048         printf("Number of inactive condition zones: %u\n", st.zc_ro_num);
1049     if (st.zc_ro_1st_lba1 > 0)
1050         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1051               st.zc_ro_1st_lba1 - 1);
1052     if (st.zc_full_num)
1053         printf("Number of full condition zones: %u\n", st.zc_full_num);
1054     if (st.zc_full_1st_lba1 > 0)
1055         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1056               st.zc_full_1st_lba1 - 1);
1057     if (st.zc_off_num)
1058         printf("Number of offline condition zones: %u\n", st.zc_off_num);
1059     if (st.zc_off_1st_lba1 > 0)
1060         printf("    Lowest starting LBA: 0x%" PRIx64 "\n",
1061               st.zc_off_1st_lba1 - 1);
1062     if (st.zc_unk_num > 0)
1063         printf("Number of unknown condition zones: %u\n", st.zc_unk_num);
1064 
1065     if (st.wp_max_lba1 > 0)
1066         printf("Highest active write pointer LBA: 0x%" PRIx64 "\n",
1067               st.wp_max_lba1 - 1);
1068     printf("Number of used blocks in write pointer zones: 0x%" PRIx64 "\n",
1069            st.wp_blk_num);
1070 
1071     if ((sg_fd >= 0) && (op->maxlen >= RCAP16_REPLY_LEN) &&
1072         ((st.wp_blk_num > 0) || (st.conv_blk_num > 0))) {
1073         uint32_t block_size = 0;
1074         uint64_t total_sz;
1075         double sz_mb, sz_gb;
1076 
1077         res = sg_ll_readcap_16(sg_fd, false, 0, rzBuff,
1078                                RCAP16_REPLY_LEN, true, op->vb);
1079         if (SG_LIB_CAT_INVALID_OP == res) {
1080             pr2serr("READ CAPACITY (16) cdb not supported\n");
1081         } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
1082             pr2serr("bad field in READ CAPACITY (16) cdb including "
1083                     "unsupported service action\n");
1084         else if (res) {
1085             sg_get_category_sense_str(res, sizeof(b), b, op->vb);
1086             pr2serr("READ CAPACITY (16) failed: %s\n", b);
1087         } else
1088             block_size = sg_get_unaligned_be32(rzBuff + 8);
1089 
1090         if (st.wp_blk_num) {
1091             total_sz = st.wp_blk_num * block_size;
1092             sz_mb = (double)(total_sz) / (double)(1048576);
1093             sz_gb = (double)(total_sz) / (double)(1000000000L);
1094 #ifdef SG_LIB_MINGW
1095             printf("   associated size: %" PRIu64 " bytes, %g MiB, %g GB",
1096                    total_sz, sz_mb, sz_gb);
1097 #else
1098             printf("   associated size: %" PRIu64 " bytes, %.1f MiB, %.2f "
1099                    "GB", total_sz, sz_mb, sz_gb);
1100 #endif
1101             if (sz_gb > 2000) {
1102 #ifdef SG_LIB_MINGW
1103                 printf(", %g TB", sz_gb / 1000);
1104 #else
1105                 printf(", %.2f TB", sz_gb / 1000);
1106 #endif
1107             }
1108             printf("\n");
1109         }
1110         if (st.conv_blk_num) {
1111             total_sz = st.conv_blk_num * block_size;
1112             sz_mb = (double)(total_sz) / (double)(1048576);
1113             sz_gb = (double)(total_sz) / (double)(1000000000L);
1114             printf("Size of all conventional zones: ");
1115 #ifdef SG_LIB_MINGW
1116             printf("%" PRIu64 " bytes, %g MiB, %g GB", total_sz, sz_mb,
1117                    sz_gb);
1118 #else
1119             printf("%" PRIu64 " bytes, %.1f MiB, %.2f GB", total_sz,
1120                    sz_mb, sz_gb);
1121 #endif
1122             if (sz_gb > 2000) {
1123 #ifdef SG_LIB_MINGW
1124                 printf(", %g TB", sz_gb / 1000);
1125 #else
1126                 printf(", %.2f TB", sz_gb / 1000);
1127 #endif
1128             }
1129             printf("\n");
1130         }
1131     }
1132     return res;
1133 }
1134 
1135 
1136 int
main(int argc,char * argv[])1137 main(int argc, char * argv[])
1138 {
1139     bool no_final_msg = false;
1140     bool as_json;
1141     int res, c, act_len, rlen, in_len, off;
1142     int sg_fd = -1;
1143     int resid = 0;
1144     int ret = 0;
1145     uint32_t decod_len;
1146     int64_t ll;
1147     const char * device_name = NULL;
1148     uint8_t * rzBuff = NULL;
1149     uint8_t * free_rzbp = NULL;
1150     const char * cmd_name = "Report zones";
1151     sgj_state * jsp;
1152     sgj_opaque_p jop = NULL;
1153     char b[80];
1154     struct opts_t opts SG_C_CPP_ZERO_INIT;
1155     struct opts_t * op = &opts;
1156 
1157     op->serv_act = REPORT_ZONES_SA;
1158     while (1) {
1159         int option_index = 0;
1160 
1161         c = getopt_long(argc, argv, "bdefF:hHi:j::l:m:n:o:prRs:SvVw",
1162                         long_options, &option_index);
1163         if (c == -1)
1164             break;
1165 
1166         switch (c) {
1167         case 'b':
1168             op->do_brief = true;
1169             break;
1170         case 'd':
1171             op->do_zdomains = true;
1172             op->serv_act = REPORT_ZONE_DOMAINS_SA;
1173             break;
1174         case 'e':
1175             op->do_realms = true;
1176             op->serv_act = REPORT_REALMS_SA;
1177             break;
1178         case 'f':
1179             op->do_force = true;
1180             break;
1181         case 'F':
1182             off = (('-' == *optarg) || ('!' == *optarg)) ? 1 : 0;
1183             if (isdigit(*(optarg + off))) {
1184                 op->find_zt = sg_get_num_nomult(optarg + off);
1185                 if (op->find_zt < 0) {
1186                     pr2serr("bad numeric argument to '--find='\n");
1187                     return SG_LIB_SYNTAX_ERROR;
1188                 }
1189                 if (off)
1190                     op->find_zt = -op->find_zt; /* find first not equal */
1191             } else {    /* check for abbreviation */
1192                 struct zt_num2abbrev_t * zn2ap = zt_num2abbrev;
1193 
1194                 for ( ; zn2ap->abbrev; ++zn2ap) {
1195                     if (0 == strcmp(optarg + off, zn2ap->abbrev))
1196                         break;
1197                 }
1198                 if (NULL == zn2ap->abbrev) {
1199                     pr2serr("bad abbreviation argument to '--find='\n\n");
1200                     prn_zone_type_abbrevs();
1201                     return SG_LIB_SYNTAX_ERROR;
1202                 }
1203                 op->find_zt = off ? -zn2ap->ztn : zn2ap->ztn;
1204             }
1205             break;
1206         case 'h':
1207         case '?':
1208             ++op->do_help;
1209             break;
1210         case 'H':
1211             ++op->do_hex;
1212             break;
1213        case 'i':
1214             op->in_fn = optarg;
1215             break;
1216        case 'j':
1217             if (! sgj_init_state(&op->json_st, optarg)) {
1218                 int bad_char = op->json_st.first_bad_char;
1219                 char e[1500];
1220 
1221                 if (bad_char) {
1222                     pr2serr("bad argument to --json= option, unrecognized "
1223                             "character '%c'\n\n", bad_char);
1224                 }
1225                 sg_json_usage(0, e, sizeof(e));
1226                 pr2serr("%s", e);
1227                 return SG_LIB_SYNTAX_ERROR;
1228             }
1229             break;
1230         /* case 'l': is under case 's': */
1231         case 'm':
1232             op->maxlen = sg_get_num(optarg);
1233             if ((op->maxlen < 0) || (op->maxlen > MAX_RZONES_BUFF_LEN)) {
1234                 pr2serr("argument to '--maxlen' should be %d or "
1235                         "less\n", MAX_RZONES_BUFF_LEN);
1236                 return SG_LIB_SYNTAX_ERROR;
1237             }
1238             op->maxlen_given = true;
1239             break;
1240         case 'n':
1241             op->do_num = sg_get_num(optarg);
1242             if (op->do_num < 0) {
1243                 pr2serr("argument to '--num' should be zero or more\n");
1244                 return SG_LIB_SYNTAX_ERROR;
1245             }
1246             break;
1247         case 'o':
1248            op->reporting_opt = sg_get_num_nomult(optarg);
1249            if ((op->reporting_opt < 0) || (op->reporting_opt > 63)) {
1250                 pr2serr("bad argument to '--report=OPT', expect 0 to "
1251                         "63\n");
1252                 return SG_LIB_SYNTAX_ERROR;
1253             }
1254             break;
1255         case 'p':
1256             op->do_partial = true;
1257             break;
1258         case 'r':
1259             op->do_raw = true;
1260             break;
1261         case 'R':
1262             op->o_readonly = true;
1263             break;
1264         case 's':
1265         case 'l':       /* --locator= and --start= are interchangeable */
1266         if ((2 == strlen(optarg)) && (0 == memcmp("-1", optarg, 2))) {
1267                 op->st_lba = UINT64_MAX;
1268                 break;
1269             }
1270             ll = sg_get_llnum(optarg);
1271             if (-1 == ll) {
1272                 pr2serr("bad argument to '--start=LBA' or '--locator=LBA\n");
1273                 return SG_LIB_SYNTAX_ERROR;
1274             }
1275             op->st_lba = (uint64_t)ll;
1276             break;
1277         case 'S':
1278             op->statistics = true;
1279             break;
1280         case 'v':
1281             op->verbose_given = true;
1282             ++op->vb;
1283             break;
1284         case 'V':
1285             op->version_given = true;
1286             break;
1287         case 'w':
1288             op->wp_only = true;
1289             break;
1290         default:
1291             pr2serr("unrecognised option code 0x%x ??\n", c);
1292             usage(1);
1293             return SG_LIB_SYNTAX_ERROR;
1294         }
1295     }
1296     if (optind < argc) {
1297         if (NULL == device_name) {
1298             device_name = argv[optind];
1299             ++optind;
1300         }
1301         if (optind < argc) {
1302             for (; optind < argc; ++optind)
1303                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
1304             usage(1);
1305             return SG_LIB_SYNTAX_ERROR;
1306         }
1307     }
1308 #ifdef DEBUG
1309     pr2serr("In DEBUG mode, ");
1310     if (op->verbose_given && op->version_given) {
1311         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1312         op->verbose_given = false;
1313         op->version_given = false;
1314         op->vb = 0;
1315     } else if (! op->verbose_given) {
1316         pr2serr("set '-vv'\n");
1317         op->vb = 2;
1318     } else
1319         pr2serr("keep verbose=%d\n", op->vb);
1320 #else
1321     if (op->verbose_given && op->version_given)
1322         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1323 #endif
1324     if (op->version_given) {
1325         pr2serr("version: %s\n", version_str);
1326         return 0;
1327     }
1328 
1329     if (op->do_help) {
1330         usage(op->do_help);
1331         return 0;
1332     }
1333     as_json = op->json_st.pr_as_json;
1334     jsp = &op->json_st;
1335     if (as_json)
1336         jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
1337 
1338     if (op->do_zdomains && op->do_realms) {
1339         pr2serr("Can't have both --domain and --realm\n");
1340         return SG_LIB_SYNTAX_ERROR;
1341     } else if (op->do_zdomains)
1342         cmd_name = "Report zone domains";
1343     else if (op->do_realms)
1344         cmd_name = "Report realms";
1345     if (as_json)
1346         sgj_js_nv_s(jsp, jop, "scsi_command_name", cmd_name);
1347     if ((op->serv_act != REPORT_ZONES_SA) && op->do_partial) {
1348         pr2serr("Can only use --partial with REPORT ZONES\n");
1349         return SG_LIB_SYNTAX_ERROR;
1350     }
1351     if (device_name && op->in_fn) {
1352         pr2serr("ignoring DEVICE, best to give DEVICE or --inhex=FN, but "
1353                 "not both\n");
1354         device_name = NULL;
1355     }
1356     if (0 == op->maxlen)
1357         op->maxlen = DEF_RZONES_BUFF_LEN;
1358     rzBuff = (uint8_t *)sg_memalign(op->maxlen, 0, &free_rzbp, op->vb > 3);
1359     if (NULL == rzBuff) {
1360         pr2serr("unable to sg_memalign %d bytes\n", op->maxlen);
1361         return sg_convert_errno(ENOMEM);
1362     }
1363 
1364     if (NULL == device_name) {
1365         if (op->in_fn) {
1366             if ((ret = sg_f2hex_arr(op->in_fn, op->do_raw, false, rzBuff,
1367                                     &in_len, op->maxlen))) {
1368                 if (SG_LIB_LBA_OUT_OF_RANGE == ret) {
1369                     no_final_msg = true;
1370                     pr2serr("... decode what we have, --maxlen=%d needs to "
1371                             "be increased\n", op->maxlen);
1372                 } else
1373                     goto the_end;
1374             }
1375             if (op->vb > 2)
1376                 pr2serr("Read %d [0x%x] bytes of user supplied data\n",
1377                         in_len, in_len);
1378             if (op->do_raw)
1379                 op->do_raw = false;    /* can interfere on decode */
1380             if (in_len < 4) {
1381                 pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
1382                         "least)\n", op->in_fn, in_len);
1383                 ret = SG_LIB_SYNTAX_ERROR;
1384                 goto the_end;
1385             }
1386             res = 0;
1387             if (op->find_zt) {  /* so '-F none' will drop through */
1388                 op->maxlen = in_len;
1389                 ret = find_report_zones(sg_fd, rzBuff, cmd_name, op, jsp);
1390                 goto the_end;
1391             } else if (op->statistics) {
1392                 op->maxlen = in_len;
1393                 ret = gather_statistics(sg_fd, rzBuff, cmd_name, op);
1394                 goto the_end;
1395             }
1396             goto start_response;
1397         } else {
1398             pr2serr("missing device name!\n\n");
1399             usage(1);
1400             ret = SG_LIB_FILE_ERROR;
1401             no_final_msg = true;
1402             goto the_end;
1403         }
1404     }
1405 
1406     if (op->do_raw) {
1407         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
1408             perror("sg_set_binary_mode");
1409             ret = SG_LIB_FILE_ERROR;
1410             goto the_end;
1411         }
1412     }
1413 
1414     sg_fd = sg_cmds_open_device(device_name, op->o_readonly, op->vb);
1415     if (sg_fd < 0) {
1416         if (op->vb)
1417             pr2serr("open error: %s: %s\n", device_name,
1418                     safe_strerror(-sg_fd));
1419         ret = sg_convert_errno(-sg_fd);
1420         goto the_end;
1421     }
1422 
1423     if (op->find_zt) {  /* so '-F none' will drop through */
1424         ret = find_report_zones(sg_fd, rzBuff, cmd_name, op, jsp);
1425         goto the_end;
1426     } else if (op->statistics) {
1427         ret = gather_statistics(sg_fd, rzBuff, cmd_name, op);
1428         goto the_end;
1429     }
1430     res = sg_ll_report_zzz(sg_fd, op->serv_act, op->st_lba, op->do_partial,
1431                            op->reporting_opt, rzBuff, op->maxlen, &resid,
1432                            true, op->vb);
1433     ret = res;
1434 start_response:
1435     if (0 == res) {
1436         rlen = op->in_fn ? in_len : (op->maxlen - resid);
1437         if (rlen < 4) {
1438             pr2serr("Decoded response length (%d) too short\n", rlen);
1439             ret = SG_LIB_CAT_MALFORMED;
1440             goto the_end;
1441         }
1442         decod_len = sg_get_unaligned_be32(rzBuff + 0) + 64;
1443         if (decod_len > WILD_RZONES_BUFF_LEN) {
1444             if (! op->do_force) {
1445                 pr2serr("decode length [%u bytes] seems wild, use --force "
1446                         "override\n", decod_len);
1447                 ret = SG_LIB_CAT_MALFORMED;
1448                 goto the_end;
1449             }
1450         }
1451         if (decod_len > (uint32_t)rlen) {
1452             if ((REPORT_ZONES_SA == op->serv_act) && (! op->do_partial)) {
1453                 pr2serr("%u zones starting from LBA 0x%" PRIx64 " available "
1454                         "but only %d zones returned\n",
1455                         (decod_len - 64) / REPORT_ZONES_DESC_LEN, op->st_lba,
1456                         (rlen - 64) / REPORT_ZONES_DESC_LEN);
1457                 decod_len = rlen;
1458                 act_len = rlen;
1459             } else {
1460                 pr2serr("decoded response length is %u bytes, but system "
1461                         "reports %d bytes received??\n", decod_len, rlen);
1462                 if (op->do_force)
1463                     act_len = rlen;
1464                 else {
1465                     pr2serr("Exiting, use --force to override\n");
1466                     ret = SG_LIB_CAT_MALFORMED;
1467                     goto the_end;
1468                 }
1469             }
1470         } else
1471             act_len = decod_len;
1472         if (op->do_raw) {
1473             dStrRaw(rzBuff, act_len);
1474             goto the_end;
1475         }
1476         if (op->do_hex && (2 != op->do_hex)) {
1477             hex2stdout(rzBuff, act_len, ((1 == op->do_hex) ? 1 : -1));
1478             goto the_end;
1479         }
1480         if (! op->wp_only && (! op->do_hex))
1481             sgj_pr_hr(jsp, "%s response:\n", cmd_name);
1482 
1483         if (act_len < 64) {
1484             pr2serr("Zone length [%d] too short (perhaps after truncation\n)",
1485                     act_len);
1486             ret = SG_LIB_CAT_MALFORMED;
1487             goto the_end;
1488         }
1489         if (REPORT_ZONES_SA == op->serv_act)
1490             ret = decode_rep_zones(rzBuff, act_len, decod_len, op, jsp);
1491         else if (op->do_realms)
1492             ret = decode_rep_realms(rzBuff, act_len, op, jsp);
1493         else if (op->do_zdomains)
1494             ret = decode_rep_zdomains(rzBuff, act_len, op, jsp);
1495     } else if (SG_LIB_CAT_INVALID_OP == res)
1496         pr2serr("%s command not supported\n", cmd_name);
1497     else {
1498         sg_get_category_sense_str(res, sizeof(b), b, op->vb);
1499         pr2serr("%s command: %s\n", cmd_name, b);
1500     }
1501 
1502 the_end:
1503     if (free_rzbp)
1504         free(free_rzbp);
1505     if (sg_fd >= 0) {
1506         res = sg_cmds_close_device(sg_fd);
1507         if (res < 0) {
1508             pr2serr("close error: %s\n", safe_strerror(-res));
1509             if (0 == ret)
1510                 ret = sg_convert_errno(-res);
1511         }
1512     }
1513     if ((0 == op->vb && (! no_final_msg))) {
1514         if (! sg_if_can2stderr("sg_rep_zones failed: ", ret))
1515             pr2serr("Some error occurred, try again with '-v' "
1516                     "or '-vv' for more information\n");
1517     }
1518     ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1519     if (as_json) {
1520         if (0 == op->do_hex)
1521             sgj_js2file(jsp, NULL, ret, stdout);
1522         sgj_finish(jsp);
1523     }
1524     return ret;
1525 }
1526