/
githubmirror
/
sg3_utils
Обзор
Документация
Войти
/
githubmirror
/
sg3_utils
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/sg_wr_mode.c
603 строки
20 KB
Douglas Gilbert
sg_wr_mode: add --mfile=MF option for decoding mask in the MF file; add inhex support for sg_wr_mode
06 сен 2023, 02:16
06 сен 2023, 02:16
7cf5d40
Код
Авторство
О чём код?
/* * Copyright (c) 2004-2023 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. * * SPDX-License-Identifier: BSD-2-Clause */ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdbool.h> #include <string.h> #include <getopt.h> #include <ctype.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_unaligned.h" #include "sg_pr2serr.h" /* A utility program originally written for the Linux OS SCSI subsystem. * * This program writes the given mode page contents to the corresponding * mode page on the given device. */ static const char * version_str = "1.32 20230901"; #define ME "sg_wr_mode: " #define MX_ALLOC_LEN 2048 #define SHORT_ALLOC_LEN 252 #define EBUFF_SZ 256 static const struct option long_options[] = { {"cfile", required_argument, 0, 'C'}, {"contents", required_argument, 0, 'c'}, {"dbd", no_argument, 0, 'd'}, {"force", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {"len", required_argument, 0, 'l'}, {"mask", required_argument, 0, 'm'}, {"mfile", required_argument, 0, 'M'}, {"page", required_argument, 0, 'p'}, {"raw", no_argument, 0, 'r'}, {"rtd", no_argument, 0, 'R'}, {"save", no_argument, 0, 's'}, {"six", no_argument, 0, '6'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; static void usage() { pr2serr("Usage: sg_wr_mode [--cfile=CF] [--contents=H,H...] [--dbd] " "[--force]\n" " [--help] [--len=10|6] [--mask=M,M...] " "[--mfile=MF]\n" " [--page=PG_H[,SPG_H]] [--raw] [--rtd] " "[--save]\n" " [--six] [--verbose] [--version] DEVICE\n" " where:\n" " --cfile=CF | -C CF contents in a file called CF\n" " --contents=H,H... | -c H,H... comma separated string " "of hex numbers\n" " that is mode page contents " "to write\n" " --contents=- | -c - read stdin for mode page contents" " to write\n" " --dbd | -d disable block descriptors (DBD bit" " in cdb)\n" " --force | -f force the contents to be written\n" " --help | -h print out usage message\n" " --len=10|6 | -l 10|6 use 10 byte (def) or 6 byte " "variants of\n" " SCSI MODE SENSE/SELECT commands\n" " --mask=M,M... | -m M,M... comma separated " "string of hex\n" " numbers that mask contents" " to write\n" " --mfile=MF | -M MF mask in a file called MF\n" " --page=PG_H | -p PG_H page_code to be written (in hex)\n" " --page=PG_H,SPG_H | -p PG_H,SPG_H page and subpage code " "to be\n" " written (in hex)\n" " --raw | -r contents of CF file decoded as " "binary\n" " --rtd | -R set RTD bit (revert to defaults) in " "cdb\n" " --save | -s set 'save page' (SP) bit; default " "don't so\n" " only 'current' values changed\n" " --six | -6 do SCSI MODE SENSE/SELECT(6) " "commands\n" " --verbose | -v increase verbosity\n" " --version | -V print version string and exit\n\n" "writes given mode page with SCSI MODE SELECT (10 or 6) " "command\n"); } /* Read hex numbers from command line or stdin. On the command line can * either be comma or space separated list. Space separated list need to be * quoted. For stdin (indicated by *inp=='-') there should be either * one entry per line, a comma separated list or space separated list. * Returns 0 if ok, or sg3_utils error code if error. */ static int build_mode_page(const char * inp, bool is_file, bool as_binary, uint8_t * mp_arr, int * mp_arr_len, int max_arr_len) { int in_len, k; unsigned int h; const char * lcp; char * cp; char * c2p; if ((NULL == inp) || (NULL == mp_arr) || (NULL == mp_arr_len)) return SG_LIB_LOGIC_ERROR; lcp = inp; in_len = strlen(inp); if (0 == in_len) *mp_arr_len = 0; if (is_file || ('-' == inp[0])) { return sg_f2hex_arr(inp, as_binary, false, mp_arr, mp_arr_len, max_arr_len); } else { /* hex string on command line */ k = strspn(inp, "0123456789aAbBcCdDeEfF, "); if (in_len != k) { pr2serr("%s: error at pos %d\n", __func__, k + 1); return SG_LIB_SYNTAX_ERROR; } for (k = 0; k < max_arr_len; ++k) { if (1 == sscanf(lcp, "%x", &h)) { if (h > 0xff) { pr2serr("%s: hex number larger than 0xff at pos %d\n", __func__, (int)(lcp - inp + 1)); return SG_LIB_SYNTAX_ERROR; } mp_arr[k] = h; cp = (char *)strchr(lcp, ','); c2p = (char *)strchr(lcp, ' '); if (NULL == cp) cp = c2p; if (NULL == cp) break; if (c2p && (c2p < cp)) cp = c2p; lcp = cp + 1; } else { pr2serr("%s: error at pos %d\n", __func__, (int)(lcp - inp + 1)); return SG_LIB_SYNTAX_ERROR; } } *mp_arr_len = k + 1; if (k == max_arr_len) { pr2serr("%s: array length exceeded\n", __func__); return SG_LIB_SYNTAX_ERROR; } } return 0; } /* Read hex numbers from command line (comma separated list). * Can also be (single) space separated list but needs to be quoted on the * command line. Returns 0 if ok, or 1 if error. */ static int build_mask(const char * inp, bool is_file, bool as_binary, uint8_t * mask_arr, int * mask_arr_len, int max_arr_len) { int in_len, k; unsigned int h; const char * lcp; char * cp; char * c2p; if ((NULL == inp) || (NULL == mask_arr) || (NULL == mask_arr_len)) return 1; lcp = inp; in_len = strlen(inp); if (0 == in_len) *mask_arr_len = 0; if (is_file || ('-' == inp[0])) { return sg_f2hex_arr(inp, as_binary, false, mask_arr, mask_arr_len, max_arr_len); } else { /* hex string on command line */ k = strspn(inp, "0123456789aAbBcCdDeEfF, "); if (in_len != k) { pr2serr("%s: error at pos %d\n", __func__, k + 1); return 1; } for (k = 0; k < max_arr_len; ++k) { if (1 == sscanf(lcp, "%x", &h)) { if (h > 0xff) { pr2serr("%s: hex number larger than 0xff at pos %d\n", __func__, (int)(lcp - inp + 1)); return 1; } mask_arr[k] = h; cp = (char *)strchr(lcp, ','); c2p = (char *)strchr(lcp, ' '); if (NULL == cp) cp = c2p; if (NULL == cp) break; if (c2p && (c2p < cp)) cp = c2p; lcp = cp + 1; } else { pr2serr("%s: error at pos %d\n", __func__, (int)(lcp - inp + 1)); return 1; } } *mask_arr_len = k + 1; if (k == max_arr_len) { pr2serr("%s: array length exceeded\n", __func__); return 1; } } return 0; } int main(int argc, char * argv[]) { bool dbd = false; bool do_raw = false; bool force = false; bool got_contents = false; bool got_mask = false; bool mode_6 = false; /* so default is mode_10 */ bool rtd = false; /* added in spc5r11 */ bool save = false; bool verbose_given = false; bool version_given = false; int res, c, num, alloc_len, off, pdt, k, md_len, hdr_len, bd_len; int mask_in_len; int sg_fd = -1; int pg_code = -1; int sub_pg_code = 0; int verbose = 0; int read_in_len = 0; int ret = 0; unsigned u, uu; const char * device_name = NULL; const char * cfile_arg = NULL; const char * contents_arg = NULL; uint8_t read_in[MX_ALLOC_LEN]; uint8_t mask_in[MX_ALLOC_LEN]; uint8_t ref_md[MX_ALLOC_LEN]; char ebuff[EBUFF_SZ]; char errStr[128]; char b[80]; struct sg_simple_inquiry_resp inq_data; static const int read_in_sz = sizeof(read_in); static const int mask_in_sz = sizeof(mask_in); static const int ref_md_sz = sizeof(ref_md); while (1) { int option_index = 0; c = getopt_long(argc, argv, "6c:C:dfhl:m:M:p:rRsvV", long_options, &option_index); if (c == -1) break; switch (c) { case '6': mode_6 = true; break; case 'c': contents_arg = optarg; break; case 'C': cfile_arg = optarg; break; case 'd': dbd = true; break; case 'f': force = true; break; case 'h': case '?': usage(); return 0; case 'l': num = sscanf(optarg, "%d", &res); if ((1 == num) && ((6 == res) || (10 == res))) mode_6 = (6 == res); else { pr2serr("length (of cdb) must be 6 or 10\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'm': memset(mask_in, 0xff, mask_in_sz); if (0 != build_mask(optarg, false, do_raw, mask_in, &mask_in_len, mask_in_sz)) { pr2serr("bad argument to '--mask'\n"); return SG_LIB_SYNTAX_ERROR; } got_mask = true; break; case 'M': if (0 != build_mask(optarg, true, do_raw, mask_in, &mask_in_len, mask_in_sz)) { pr2serr("bad argument or unable to decode '--mfile'\n"); return SG_LIB_SYNTAX_ERROR; } got_mask = true; break; case 'p': if (NULL == strchr(optarg, ',')) { num = sscanf(optarg, "%x", &u); if ((1 != num) || (u > 62)) { pr2serr("Bad hex page code value after '--page' " "switch\n"); return SG_LIB_SYNTAX_ERROR; } pg_code = u; } else if (2 == sscanf(optarg, "%x,%x", &u, &uu)) { if (uu > 254) { pr2serr("Bad hex sub page code value after '--page' " "switch\n"); return SG_LIB_SYNTAX_ERROR; } pg_code = u; sub_pg_code = uu; } else { pr2serr("Bad hex page code, subpage code sequence after " "'--page' switch\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'r': do_raw = true; break; case 'R': rtd = true; break; case 's': save = true; break; case 'v': verbose_given = true; ++verbose; break; case 'V': version_given = true; break; default: pr2serr("unrecognised option code 0x%x ??\n", c); usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if (NULL == device_name) { device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } if (cfile_arg || contents_arg) { const char * inp = (cfile_arg ? cfile_arg : contents_arg); if (cfile_arg && contents_arg) { pr2serr("Cannot have both --contents= and --cfile= options\n"); return SG_LIB_SYNTAX_ERROR; } memset(read_in, 0, read_in_sz); if ((ret = build_mode_page(inp, !! cfile_arg, do_raw, read_in, &read_in_len, read_in_sz))) { pr2serr("bad argument to '%s'\n", cfile_arg ? "--cfile=" : "--contents="); return ret; } if (verbose > 5) { pr2serr("Decoded contents:\n"); hex2stderr(read_in, read_in_len, 1); } got_contents = true; } #ifdef DEBUG pr2serr("In DEBUG mode, "); if (verbose_given && version_given) { pr2serr("but override: '-vV' given, zero verbose and continue\n"); /* verbose_given = false; */ version_given = false; verbose = 0; } else if (! verbose_given) { pr2serr("set '-vv'\n"); verbose = 2; } else pr2serr("keep verbose=%d\n", verbose); #else if (verbose_given && version_given) pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); #endif if (version_given) { pr2serr(ME "version: %s\n", version_str); return 0; } if (NULL == device_name) { pr2serr("missing device name!\n\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if ((pg_code < 0) && (! rtd)) { pr2serr("need page code (see '--page=')\n\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if (got_mask && force) { pr2serr("cannot use both '--force' and '--mask'\n\n"); usage(); return SG_LIB_CONTRADICT; } sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose); if (sg_fd < 0) { if (verbose) pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd)); ret = sg_convert_errno(-sg_fd); goto fini; } if (rtd) goto revert_to_defaults; if (0 == sg_simple_inquiry(sg_fd, &inq_data, false, verbose)) pdt = inq_data.peripheral_type; else pdt = PDT_UNKNOWN; /* do MODE SENSE to fetch current values */ memset(ref_md, 0, ref_md_sz); snprintf(errStr, sizeof(errStr), "MODE SENSE (%d): ", mode_6 ? 6 : 10); alloc_len = mode_6 ? SHORT_ALLOC_LEN : MX_ALLOC_LEN; if (mode_6) res = sg_ll_mode_sense6(sg_fd, dbd, 0 /*current */, pg_code, sub_pg_code, ref_md, alloc_len, true, verbose); else res = sg_ll_mode_sense10(sg_fd, false /* llbaa */, dbd, 0 /* current */, pg_code, sub_pg_code, ref_md, alloc_len, true, verbose); ret = res; if (res) { if (SG_LIB_CAT_INVALID_OP == res) pr2serr("%snot supported, try '--len=%d'\n", errStr, (mode_6 ? 10 : 6)); else { sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("%s%s\n", errStr, b); } goto fini; } off = sg_mode_page_offset(ref_md, alloc_len, mode_6, ebuff, EBUFF_SZ); if (off < 0) { pr2serr("%s%s\n", errStr, ebuff); goto fini; } md_len = sg_msense_calc_length(ref_md, alloc_len, mode_6, &bd_len); if (md_len < 0) { pr2serr("%ssg_msense_calc_length() failed\n", errStr); goto fini; } hdr_len = mode_6 ? 4 : 8; if (got_contents) { if (read_in_len < 2) { pr2serr("contents length=%d too short\n", read_in_len); goto fini; } ref_md[0] = 0; /* mode data length reserved for mode select */ if (! mode_6) ref_md[1] = 0; /* mode data length reserved for mode select */ if (0 == pdt) /* for disks mask out DPOFUA bit */ ref_md[mode_6 ? 2 : 3] &= 0xef; if (md_len > alloc_len) { pr2serr("mode data length=%d exceeds allocation length=%d\n", md_len, alloc_len); goto fini; } if (got_mask) { for (k = 0; k < (md_len - off); ++k) { if ((0x0 == mask_in[k]) || (k > read_in_len)) read_in[k] = ref_md[off + k]; else if (mask_in[k] < 0xff) { c = (ref_md[off + k] & (0xff & ~mask_in[k])); read_in[k] = (c | (read_in[k] & mask_in[k])); } } read_in_len = md_len - off; } if (! force) { if ((! (ref_md[off] & 0x80)) && save) { pr2serr("PS bit in existing mode page indicates that it is " "not saveable\n but '--save' option given\n"); goto fini; } read_in[0] &= 0x7f; /* mask out PS bit, reserved in mode select */ if ((md_len - off) != read_in_len) { pr2serr("contents length=%d but reference mode page " "length=%d\n", read_in_len, md_len - off); goto fini; } if (pg_code != (read_in[0] & 0x3f)) { pr2serr("contents page_code=0x%x but reference " "page_code=0x%x\n", (read_in[0] & 0x3f), pg_code); goto fini; } if ((read_in[0] & 0x40) != (ref_md[off] & 0x40)) { pr2serr("contents flags subpage but reference page does not " "(or vice versa)\n"); goto fini; } if ((read_in[0] & 0x40) && (read_in[1] != sub_pg_code)) { pr2serr("contents subpage_code=0x%x but reference " "sub_page_code=0x%x\n", read_in[1], sub_pg_code); goto fini; } } else md_len = off + read_in_len; /* force length */ memcpy(ref_md + off, read_in, read_in_len); if (mode_6) res = sg_ll_mode_select6_v2(sg_fd, true /* PF */, rtd, save, ref_md, md_len, true, verbose); else res = sg_ll_mode_select10_v2(sg_fd, true /* PF */, rtd, save, ref_md, md_len, true, verbose); ret = res; if (res) goto fini; } else { printf(">>> No contents given, so show current mode page data:\n"); printf(" header:\n"); hex2stdout(ref_md, hdr_len, -1); if (bd_len) { printf(" block descriptor(s):\n"); hex2stdout(ref_md + hdr_len, bd_len, -1); } else printf(" << no block descriptors >>\n"); printf(" mode page:\n"); hex2stdout(ref_md + off, md_len - off, -1); } ret = 0; goto fini; revert_to_defaults: if (verbose) pr2serr("Doing MODE SELECT(%d) with revert to defaults (RTD) set " "and SP=%d\n", mode_6 ? 6 : 10, !! save); if (mode_6) res = sg_ll_mode_select6_v2(sg_fd, false /* PF */, true /* rtd */, save, NULL, 0, true, verbose); else res = sg_ll_mode_select10_v2(sg_fd, false /* PF */, true /* rtd */, save, NULL, 0, true, verbose); ret = res; fini: if (sg_fd >= 0) { res = sg_cmds_close_device(sg_fd); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == ret) ret = sg_convert_errno(-res); } } if (0 == verbose) { if (! sg_if_can2stderr("sg_wr_mode failed: ", ret)) pr2serr("Some error occurred, try again with '-v' or '-vv' for " "more information\n"); } return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; }