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