diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2017-12-07 13:32:05 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2017-12-07 13:32:05 +0000 |
commit | efe114304fdf1c20afd491eacfd1aade4b136ca5 (patch) | |
tree | b1672761995949e914d9821fa33b157ed1ce80ab /src | |
parent | 0693a6de283a5802c480ff352b2933b87020cd42 (diff) | |
download | sg3_utils-efe114304fdf1c20afd491eacfd1aade4b136ca5.tar.gz |
add include/sg_pt_linux.h lib/sg_pt_linux_nvme.c and .gitignore; sg_write_x: almost finished; more NVMe work (for sg_ses)
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@733 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src')
-rw-r--r-- | src/sg_dd.c | 25 | ||||
-rw-r--r-- | src/sg_inq.c | 83 | ||||
-rw-r--r-- | src/sg_raw.c | 65 | ||||
-rw-r--r-- | src/sg_ses.c | 83 | ||||
-rw-r--r-- | src/sg_vpd.c | 10 | ||||
-rw-r--r-- | src/sg_write_x.c | 574 |
6 files changed, 520 insertions, 320 deletions
diff --git a/src/sg_dd.c b/src/sg_dd.c index 736d1e05..74b0547e 100644 --- a/src/sg_dd.c +++ b/src/sg_dd.c @@ -62,7 +62,7 @@ #include "sg_unaligned.h" #include "sg_pr2serr.h" -static const char * version_str = "5.92 20171023"; +static const char * version_str = "5.93 20171206"; #define ME "sg_dd: " @@ -1838,7 +1838,7 @@ main(int argc, char * argv[]) } if (iflag.dio || iflag.direct || oflag.direct || (FT_RAW & in_type) || - (FT_RAW & out_type)) { + (FT_RAW & out_type)) { /* want heap buffer aligned to page_size */ size_t psz; #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) @@ -1847,26 +1847,11 @@ main(int argc, char * argv[]) psz = 4096; /* give up, pick likely figure */ #endif -#ifdef HAVE_POSIX_MEMALIGN - { - int err; - - err = posix_memalign((void **)&wrkBuff, psz, blk_sz * bpt); - if (err) { - pr2serr("posix_memalign: error [%d] out of memory?\n", err); - return SG_LIB_CAT_OTHER; - } - wrkPos = wrkBuff; - } -#else - wrkBuff = (unsigned char*)malloc(blk_sz * bpt + psz); - if (0 == wrkBuff) { - pr2serr("Not enough user memory for work buffer\n"); + wrkPos = sg_memalign(blk_sz * bpt, psz, &wrkBuff, verbose > 3); + if (NULL == wrkPos) { + pr2serr("sg_memalign: error, out of memory?\n"); return SG_LIB_CAT_OTHER; } - wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) & - (~(psz - 1))); -#endif } else { wrkBuff = (unsigned char*)malloc(blk_sz * bpt); if (0 == wrkBuff) { diff --git a/src/sg_inq.c b/src/sg_inq.c index 6cdaf757..29dcc7aa 100644 --- a/src/sg_inq.c +++ b/src/sg_inq.c @@ -46,7 +46,7 @@ #include "sg_pt_nvme.h" #endif -static const char * version_str = "1.73 20171115"; /* SPC-5 rev 17 */ +static const char * version_str = "1.74 20171206"; /* SPC-5 rev 17 */ /* INQUIRY notes: * It is recommended that the initial allocation length given to a @@ -300,7 +300,8 @@ usage() " only supported for VPD pages 0x80 and 0x83\n" " --extended|-E|-x decode extended INQUIRY data VPD page " "(0x86)\n" - " --force|-f skip VPD page 0 checking\n" + " --force|-f skip VPD page 0 checking; provide more " + "NVMe info\n" " --help|-h print usage message then exit\n" " --hex|-H output response in hex\n" " --id|-i decode device identification VPD page " @@ -1748,7 +1749,7 @@ decode_dev_ids(const char * leadin, unsigned char * buff, int len, int do_hex, "identifier\n", sg_get_trans_proto_str(p_id, sizeof(b), b)); break; - case 0xa: /* UUID identifier [spc5r08] */ + case 0xa: /* UUID identifier [spc5r08] RFC 4122 */ if (1 != c_set) { pr2serr(" << expected binary code_set >>\n"); dStrHexErr((const char *)ip, i_len, 0); @@ -3793,7 +3794,7 @@ const char * rperf[] = {"Best", "Better", "Good", "Degraded"}; static int do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid, - struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_din, + struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_dinp, int id_din_len, const struct opts_t * op) { bool got_eui_128 = false; @@ -3806,7 +3807,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid, clear_scsi_pt_obj(ptvp); id_cmdp->nsid = nsid; id_cmdp->cdw10 = 0x0; /* CNS=0x0 Identify NS */ - set_scsi_pt_data_in(ptvp, id_din, id_din_len); + set_scsi_pt_data_in(ptvp, id_dinp, id_din_len); set_scsi_pt_sense(ptvp, (unsigned char *)&cmd_back, sizeof(cmd_back)); set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp)); ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb); @@ -3817,24 +3818,24 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid, } if (ret) return ret; - num_lbaf = id_din[25] + 1; /* spec says this is "0's based value" */ - flbas = id_din[26] & 0xf; /* index of active LBA format (for this ns) */ + num_lbaf = id_dinp[25] + 1; /* spec says this is "0's based value" */ + flbas = id_dinp[26] & 0xf; /* index of active LBA format (for this ns) */ if (op->do_hex || op->do_raw) { - do_nvme_identify_hex_raw(id_din, sizeof(id_din), op); + do_nvme_identify_hex_raw(id_dinp, id_din_len, op); return ret; } - ns_sz = sg_get_unaligned_le64(id_din + 0); - eui_64 = sg_get_unaligned_be64(id_din + 120); /* N.B. big endian */ - if (! sg_all_zeros(id_din + 104, 16)) + ns_sz = sg_get_unaligned_le64(id_dinp + 0); + eui_64 = sg_get_unaligned_be64(id_dinp + 120); /* N.B. big endian */ + if (! sg_all_zeros(id_dinp + 104, 16)) got_eui_128 = true; printf(" Namespace size/capacity: %" PRIu64 "/%" PRIu64 - " blocks\n", ns_sz, sg_get_unaligned_le64(id_din + 8)); + " blocks\n", ns_sz, sg_get_unaligned_le64(id_dinp + 8)); printf(" Namespace utilization: %" PRIu64 " blocks\n", - sg_get_unaligned_le64(id_din + 16)); + sg_get_unaligned_le64(id_dinp + 16)); if (got_eui_128) { /* N.B. big endian */ - printf(" NGUID: 0x%02x", id_din[104]); + printf(" NGUID: 0x%02x", id_dinp[104]); for (k = 1; k < 16; ++k) - printf("%02x", id_din[104 + k]); + printf("%02x", id_dinp[104 + k]); printf("\n"); } else if (op->do_force) printf(" NGUID: 0x0\n"); @@ -3848,7 +3849,7 @@ do_nvme_id_ns(struct sg_pt_base * ptvp, uint32_t nsid, printf(" <-- active\n"); else printf("\n"); - flba_info = sg_get_unaligned_le32(id_din + off); + flba_info = sg_get_unaligned_le32(id_dinp + off); md_size = flba_info & 0xffff; lb_size = flba_info >> 16 & 0xff; if (lb_size > 31) { @@ -3889,7 +3890,9 @@ do_nvme_identify(int pt_fd, const struct opts_t * op) struct sg_nvme_passthru_cmd identify_cmd; struct sg_nvme_passthru_cmd cmd_back; struct sg_nvme_passthru_cmd * id_cmdp = &identify_cmd; - uint8_t id_din[4096]; + uint8_t * id_dinp = NULL; + uint8_t * free_id_dinp = NULL; + const uint32_t pg_sz = sg_get_page_size(); if (op->do_raw) { if (sg_set_binary_mode(STDOUT_FILENO) < 0) { @@ -3906,7 +3909,8 @@ do_nvme_identify(int pt_fd, const struct opts_t * op) id_cmdp->opcode = 0x6; nsid = get_pt_nvme_nsid(ptvp); id_cmdp->cdw10 = 0x1; /* CNS=0x1 Identify controller */ - set_scsi_pt_data_in(ptvp, id_din, sizeof(id_din)); + id_dinp = sg_memalign(pg_sz, pg_sz, &free_id_dinp, vb > 3); + set_scsi_pt_data_in(ptvp, id_dinp, pg_sz); set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp)); set_scsi_pt_sense(ptvp, (unsigned char *)&cmd_back, sizeof(cmd_back)); ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb); @@ -3917,16 +3921,16 @@ do_nvme_identify(int pt_fd, const struct opts_t * op) } if (ret) goto err_out; - max_nsid = sg_get_unaligned_le16(id_din + 516); + max_nsid = sg_get_unaligned_le16(id_dinp + 516); if (op->do_raw || op->do_hex) { - do_nvme_identify_hex_raw(id_din, sizeof(id_din), op); + do_nvme_identify_hex_raw(id_dinp, pg_sz, op); goto skip1; } printf("Identify controller for %s:\n", op->device_name); - printf(" Model number: %.40s\n", (const char *)(id_din + 24)); - printf(" Serial number: %.20s\n", (const char *)(id_din + 4)); - printf(" Firmware revision: %.8s\n", (const char *)(id_din + 64)); - ver = sg_get_unaligned_le32(id_din + 80); + printf(" Model number: %.40s\n", (const char *)(id_dinp + 24)); + printf(" Serial number: %.20s\n", (const char *)(id_dinp + 4)); + printf(" Firmware revision: %.8s\n", (const char *)(id_dinp + 64)); + ver = sg_get_unaligned_le32(id_dinp + 80); ver_maj = (ver >> 16); ver_min = (ver >> 8) & 0xff; ver_ter = (ver & 0xff); @@ -3937,28 +3941,28 @@ do_nvme_identify(int pt_fd, const struct opts_t * op) else printf("\n"); printf(" PCI vendor ID VID/SSVID: 0x%x/0x%x\n", - sg_get_unaligned_le16(id_din + 0), - sg_get_unaligned_le16(id_din + 2)); + sg_get_unaligned_le16(id_dinp + 0), + sg_get_unaligned_le16(id_dinp + 2)); printf(" IEEE OUI Identifier: 0x%x\n", - sg_get_unaligned_le24(id_din + 73)); - got_fguid = ! sg_all_zeros(id_din + 112, 16); + sg_get_unaligned_le24(id_dinp + 73)); + got_fguid = ! sg_all_zeros(id_dinp + 112, 16); if (got_fguid) { - printf(" FGUID: 0x%02x", id_din[112]); + printf(" FGUID: 0x%02x", id_dinp[112]); for (k = 1; k < 16; ++k) - printf("%02x", id_din[112 + k]); + printf("%02x", id_dinp[112 + k]); printf("\n"); } else if (op->do_force) printf(" FGUID: 0x0\n"); - printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_din + 78)); + printf(" Controller ID: 0x%x\n", sg_get_unaligned_le16(id_dinp + 78)); if (op->do_force) { printf(" Management endpoint capabilities, over a PCIe port: %d\n", - !! (0x2 & id_din[255])); + !! (0x2 & id_dinp[255])); printf(" Management endpoint capabilities, over a SMBus/I2C port: " - "%d\n", !! (0x1 & id_din[255])); + "%d\n", !! (0x1 & id_dinp[255])); } printf(" Number of namespaces: %u\n", max_nsid); - sz1 = sg_get_unaligned_le64(id_din + 280); /* lower 64 bits */ - sz2 = sg_get_unaligned_le64(id_din + 288); /* upper 64 bits */ + sz1 = sg_get_unaligned_le64(id_dinp + 280); /* lower 64 bits */ + sz2 = sg_get_unaligned_le64(id_dinp + 288); /* upper 64 bits */ if (sz2) printf(" Total NVM capacity: huge ...\n"); else if (sz1) @@ -3969,8 +3973,8 @@ do_nvme_identify(int pt_fd, const struct opts_t * op) const char * cp; printf(" Total NVM capacity: 0 bytes\n"); - npss = id_din[263] + 1; - up = id_din + 2048; + npss = id_dinp[263] + 1; + up = id_dinp + 2048; for (k = 0; k < npss; ++k, up += 32) { n = sg_get_unaligned_le16(up + 0); n *= (0x1 & up[3]) ? 1 : 100; /* unit: 100 microWatts */ @@ -4013,7 +4017,7 @@ skip1: pr2serr("NSID from device (%u) should not exceed number of " "namespaces (%u)\n", nsid, max_nsid); } - ret = do_nvme_id_ns(ptvp, nsid, id_cmdp, id_din, sizeof(id_din), op); + ret = do_nvme_id_ns(ptvp, nsid, id_cmdp, id_dinp, pg_sz, op); if (ret) goto err_out; @@ -4021,13 +4025,14 @@ skip1: for (k = 1; k <= max_nsid; ++k) { if ((! op->do_raw) || (op->do_hex < 3)) printf(" Namespace %u (of %u):\n", k, max_nsid); - ret = do_nvme_id_ns(ptvp, k, id_cmdp, id_din, sizeof(id_din), op); + ret = do_nvme_id_ns(ptvp, k, id_cmdp, id_dinp, pg_sz, op); if (ret) goto err_out; } } err_out: destruct_scsi_pt_obj(ptvp); + free(free_id_dinp); return ret; } diff --git a/src/sg_raw.c b/src/sg_raw.c index 0f513e4c..537b9b1d 100644 --- a/src/sg_raw.c +++ b/src/sg_raw.c @@ -32,7 +32,7 @@ #include "sg_pr2serr.h" #include "sg_unaligned.h" -#define SG_RAW_VERSION "0.4.18 (2017-10-09)" +#define SG_RAW_VERSION "0.4.19 (2017-12-06)" #ifdef SG_LIB_WIN32 #ifndef HAVE_SYSCONF @@ -275,63 +275,6 @@ process_cl(struct opts_t * op, int argc, char *argv[]) return 0; } -/* Allocate aligned memory (heap) starting on page boundary */ -static unsigned char * -my_memalign(int length, unsigned char ** wrkBuffp, const struct opts_t * op) -{ - size_t psz; - unsigned char * res; - -#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) - psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */ -#elif defined(SG_LIB_WIN32) - psz = win_pagesize(); -#else - psz = 4096; /* give up, pick likely figure */ -#endif - -#ifdef HAVE_POSIX_MEMALIGN - { - int err; - void * wp = NULL; - - err = posix_memalign(&wp, psz, length); - if (err || (NULL == wp)) { - pr2serr("posix_memalign: error [%d], out of memory?\n", err); - return NULL; - } - memset(wp, 0, length); - if (wrkBuffp) - *wrkBuffp = (unsigned char *)wp; - res = (unsigned char *)wp; - if (op->verbose > 3) - pr2serr("%s: posix, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n", - __func__, length, (void *)*wrkBuffp, (int)psz, - (void *)res); - return res; - } -#else - { - unsigned char * wrkBuff; - - wrkBuff = (unsigned char*)calloc(length + psz, 1); - if (NULL == wrkBuff) { - if (wrkBuffp) - *wrkBuffp = NULL; - return NULL; - } else if (wrkBuffp) - *wrkBuffp = wrkBuff; - res = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) & - (~(psz - 1))); - if (op->verbose > 3) - pr2serr("%s: hack, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n", - __func__, length, (void *)*wrkBuffp, (int)psz, - (void *)res); - return res; - } -#endif -} - static int skip(int fd, off_t offset) { @@ -392,7 +335,8 @@ fetch_dataout(struct opts_t * op) } } - buf = my_memalign(op->dataout_len, &wrkBuf, op); + buf = sg_memalign(op->dataout_len, 0 /* page_size */, &wrkBuf, + op->verbose > 3); if (buf == NULL) { perror("malloc"); goto bail; @@ -528,7 +472,8 @@ main(int argc, char *argv[]) set_scsi_pt_data_out(ptvp, dxfer_buffer_out, op->dataout_len); } if (op->do_datain) { - dxfer_buffer_in = my_memalign(op->datain_len, &wrkBuf, op); + dxfer_buffer_in = sg_memalign(op->datain_len, 0 /* page_size */, + &wrkBuf, op->verbose > 3); if (dxfer_buffer_in == NULL) { perror("malloc"); ret = SG_LIB_CAT_OTHER; diff --git a/src/sg_ses.c b/src/sg_ses.c index ce977ecd..80153b3f 100644 --- a/src/sg_ses.c +++ b/src/sg_ses.c @@ -314,27 +314,11 @@ static struct join_row_t join_arr[MX_JOIN_ROWS]; static struct join_row_t * join_arr_lastp = join_arr + MX_JOIN_ROWS - 1; static bool join_done = false; -#ifdef SG_LIB_FREEBSD - -#include <sys/param.h> /* contains PAGE_SIZE */ - -static uint8_t enc_stat_rsp[MX_ALLOC_LEN] - __attribute__ ((aligned (PAGE_SIZE))); -static uint8_t elem_desc_rsp[MX_ALLOC_LEN] - __attribute__ ((aligned (PAGE_SIZE))); -static uint8_t add_elem_rsp[MX_ALLOC_LEN] - __attribute__ ((aligned (PAGE_SIZE))); -static uint8_t threshold_rsp[MX_ALLOC_LEN] - __attribute__ ((aligned (PAGE_SIZE))); - -#else - -static uint8_t enc_stat_rsp[MX_ALLOC_LEN]; -static uint8_t elem_desc_rsp[MX_ALLOC_LEN]; -static uint8_t add_elem_rsp[MX_ALLOC_LEN]; -static uint8_t threshold_rsp[MX_ALLOC_LEN]; - -#endif +/* Large buffers on heap, aligned to page size and zeroed */ +static uint8_t * enc_stat_rsp; +static uint8_t * elem_desc_rsp; +static uint8_t * add_elem_rsp; +static uint8_t * threshold_rsp; static int enc_stat_rsp_len; static int elem_desc_rsp_len; @@ -5096,24 +5080,31 @@ enumerate_work(const struct opts_t * op) int main(int argc, char * argv[]) { - int sg_fd, k, res; - int pd_type = 0; bool have_cgs = false; + int k, res; + int sg_fd = -1; + int pd_type = 0; int ret = 0; - struct sg_simple_inquiry_resp inq_resp; - char buff[128]; - char b[80]; - struct tuple_acronym_val tav_arr[CGS_CL_ARR_MAX_SZ]; + uint32_t pg_sz; const char * cp; struct opts_t opts; struct opts_t * op; struct tuple_acronym_val * tavp; struct cgs_cl_t * cgs_clp; + uint8_t * free_enc_stat_rsp = NULL; + uint8_t * free_elem_desc_rsp = NULL; + uint8_t * free_add_elem_rsp = NULL; + uint8_t * free_threshold_rsp = NULL; + struct tuple_acronym_val tav_arr[CGS_CL_ARR_MAX_SZ]; + struct sg_simple_inquiry_resp inq_resp; + char buff[128]; + char b[80]; op = &opts; memset(op, 0, sizeof(*op)); op->dev_slot_num = -1; op->ind_indiv_last = -1; + pg_sz = sg_get_page_size(); res = cl_process(op, argc, argv); if (res) return SG_LIB_SYNTAX_ERROR; @@ -5129,6 +5120,31 @@ main(int argc, char * argv[]) enumerate_work(op); return 0; } + enc_stat_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_enc_stat_rsp, + op->verbose > 3); + if (NULL == enc_stat_rsp) { + pr2serr("Unable to get heap for enc_stat_rsp\n"); + goto err_out; + } + elem_desc_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_elem_desc_rsp, + op->verbose > 3); + if (NULL == elem_desc_rsp) { + pr2serr("Unable to get heap for elem_desc_rsp\n"); + goto err_out; + } + add_elem_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_add_elem_rsp, + op->verbose > 3); + if (NULL == add_elem_rsp) { + pr2serr("Unable to get heap for add_elem_rsp\n"); + goto err_out; + } + threshold_rsp = sg_memalign(MX_ALLOC_LEN, pg_sz, &free_threshold_rsp, + op->verbose > 3); + if (NULL == threshold_rsp) { + pr2serr("Unable to get heap for threshold_rsp\n"); + goto err_out; + } + if (op->num_cgs) { have_cgs = true; if (op->page_code_given && @@ -5332,7 +5348,18 @@ err_out: if (ret && (0 == op->verbose)) pr2serr("Problem detected, try again with --verbose option for more " "information\n"); - res = sg_cmds_close_device(sg_fd); + if (sg_fd >= 0) + res = sg_cmds_close_device(sg_fd); + else + res = 0; + if (free_enc_stat_rsp) + free(free_enc_stat_rsp); + if (free_elem_desc_rsp) + free(free_elem_desc_rsp); + if (free_add_elem_rsp) + free(free_add_elem_rsp); + if (free_threshold_rsp) + free(free_threshold_rsp); if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == ret) diff --git a/src/sg_vpd.c b/src/sg_vpd.c index 728c7f9b..a3cf9fe9 100644 --- a/src/sg_vpd.c +++ b/src/sg_vpd.c @@ -38,7 +38,7 @@ */ -static const char * version_str = "1.31 20171104"; /* spc5r17 + sbc4r14 */ +static const char * version_str = "1.32 20171206"; /* spc5r17 + sbc4r14 */ /* standard VPD pages, in ascending page number order */ #define VPD_SUPPORTED_VPDS 0x0 @@ -1086,7 +1086,7 @@ decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc, break; case 9: /* Protocol specific port identifier */ break; - case 0xa: /* UUID identifier */ + case 0xa: /* UUID identifier [spc5r08] RFC 4122 */ if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf))) break; for (m = 0; m < 16; ++m) { @@ -2261,10 +2261,10 @@ decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt) printf(" Manufacturer-assigned serial number: %.*s\n", len - 4, buff + 4); break; - case PDT_SES: /* T10/17-142r1 -> ses4r02 ?? */ + case PDT_SES: /* T10/17-142r1 -> ses4r02 ?? */ if (len < 8) { pr2serr("Enclosure service device characteristics VPD page " - "length too short=%d\n", len); + "length too short=%d\n", len); return; } printf(" SESDNLD=%d\n", !! (0x2 & buff[4])); @@ -2276,7 +2276,7 @@ decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt) printf(" DMOSASDS=%d\n", !! (0x8 & buff[6])); printf(" DMOSDS=%d\n", !! (0x4 & buff[6])); printf(" ADMS=%d\n", !! (0x1 & buff[6])); - break; + break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); dStrHexErr((const char *)buff, len, 0); diff --git a/src/sg_write_x.c b/src/sg_write_x.c index 4c4207c3..12d0b630 100644 --- a/src/sg_write_x.c +++ b/src/sg_write_x.c @@ -37,10 +37,17 @@ #include "sg_unaligned.h" #include "sg_pr2serr.h" -static const char * version_str = "1.05 20171127"; +static const char * version_str = "1.05 20171202"; - -#define ME "sg_write_x: " +/* Protection Information refers to 8 bytes of extra information usually + * associated with each logical block and is often abbreviated to PI while + * its fields: reference-tag (4 bytes), application-tag (2 bytes) and + * tag-mask (2 bytes) are often abbreviated to RT, AT and TM respectively. + * And the LBA Range Descriptor associated with the WRITE SCATTERED command + * is abbreviated to RD. A degenerate RD is one where both the LBA and length + * components are zero; they are not illegal according to T10 but are a + * little tricky to handle when scanning and little extra information + * is provided. */ #define ORWRITE16_OP 0x8b #define WRITE_16_OP 0x8a @@ -102,12 +109,13 @@ static struct option long_options[] = { {"num", required_argument, 0, 'n'}, {"offset", required_argument, 0, 'o'}, {"or", no_argument, 0, 'O'}, - {"raw", no_argument, 0, 'r'}, - {"ref-tag", required_argument, 0, 'R'}, - {"ref_tag", required_argument, 0, 'R'}, + {"ref-tag", required_argument, 0, 'r'}, + {"ref_tag", required_argument, 0, 'r'}, {"same", required_argument, 0, 'M'}, {"scat-file", required_argument, 0, 'q'}, {"scat_file", required_argument, 0, 'q'}, + {"scat-raw", no_argument, 0, 'R'}, + {"scat_raw", no_argument, 0, 'R'}, {"scattered", required_argument, 0, 'S'}, {"stream", required_argument, 0, 'T'}, {"strict", no_argument, 0, 's'}, @@ -130,11 +138,11 @@ struct opts_t { bool do_combined; /* -c DOF --> .scat_lbdof */ bool do_dry_run; bool do_or; /* -O ORWRITE(16 or 32) */ - bool do_raw; + bool do_scat_raw; bool do_same; /* -M WRITE SAME(16 or 32) */ /* --same=NDOB NDOB --> .ndob */ bool do_scattered; /* -S WRITE SCATTERED(16 or 32) */ - /* --scattered=RD RD --> .scat_num_lbard */ + /* --scattered=RD RD --> .scat_num_lbrd */ bool do_stream; /* -T WRITE STREAM(16 or 32) */ /* --stream=ID ID --> .str_id */ bool do_unmap; @@ -143,6 +151,7 @@ struct opts_t { * is 8 bytes long following each logical * block in the data out buffer. */ bool dpo; + bool explicit_lba; /* .numblocks defaults to 0 when false */ bool fua; bool ndob; bool strict; @@ -158,19 +167,21 @@ struct opts_t { uint16_t app_tag; /* part of protection information (def: 0xffff) */ uint16_t atomic_boundary; uint16_t scat_lbdof; - uint16_t scat_num_lbard; + uint16_t scat_num_lbrd; uint16_t str_id; /* (stream ID) is for WRITE STREAM */ uint16_t tag_mask; /* part of protection information (def: 0xffff) */ uint32_t bs; /* logical block size (def: 0). 0 implies use READ * CAPACITY(10 or 16) to determine */ uint32_t bs_pi_do; /* logical block size plus PI, if any */ - uint32_t numblocks; + uint32_t if_dlen; /* bytes to read after .if_offset from .if_name, + * if 0 given, read rest of .if_name */ + uint32_t numblocks; /* defaults to 0 if .explicit_lba is false, else + * derives from IF and/or sum of NUMs */ uint32_t orw_eog; uint32_t orw_nog; uint32_t ref_tag; /* part of protection information (def: 0xffffffff) */ uint64_t lba; - uint64_t offset; /* byte offset in if_name to start reading */ - uint64_t dlen; /* bytes to read after offset from if_name, 0->rest */ + uint64_t if_offset; /* byte offset in .if_name to start reading */ uint64_t tot_lbs; /* from READ CAPACITY */ ssize_t xfer_bytes; /* derived value: bs_pi_do * numblocks */ const char * device_name; @@ -193,24 +204,24 @@ usage(int do_help) " [--fua] [--generation=EOG,NOG] [--grpnum=GN] " "[--help] --in=IF\n" " [--lba=LBA,LBA...] [--normal] [--num=NUM,NUM...]\n" - " [--offset=OFF[,DLEN]] [--or] [--raw] [--ref-tag=RT] " + " [--offset=OFF[,DLEN]] [--or] [--ref-tag=RT] " "[--same=NDOB]\n" - " [--scat-file=SF] [--scattered=RD] [--stream=ID] " - "[--strict]\n" - " [--tag-mask=TM] [--timeout=TO] [--unmap=U_A] " - "[--verbose]\n" - " [--version] [--wrprotect=WRP] DEVICE\n"); + " [--scat-file=SF] [--scat-raw] [--scattered=RD] " + "[--stream=ID]\n" + " [--strict] [--tag-mask=TM] [--timeout=TO] " + "[--unmap=U_A]\n" + " [--verbose] [--version] [--wrprotect=WRP] DEVICE\n"); if (1 != do_help) { pr2serr("\nOr the corresponding short option usage:\n" "sg_write_x [-6] [-3] [-a AT] [-A AB] [-B OP,PGP] [-b LBS] " "[-c DOF] [-D DLD]\n" " [-d] [-x] [-f] [-G EOG,NOG] [-g GN] [-h] -i IF " "[-l LBA,LBA...]\n" - " [-N] [-n NUM,NUM...] [-o OFF[,DLEN]] [-O] [-r] " - "[-R RT] [-M NDOB]\n" - " [-q SF] [-S RD] [-T ID] [-s] [-t TM] [-I TO] " - "[-u U_A] [-v] [-V]\n" - " [-w WPR] DEVICE\n" + " [-N] [-n NUM,NUM...] [-o OFF[,DLEN]] [-O] " + "[-r RT] [-M NDOB]\n" + " [-q SF] [-R] [-S RD] [-T ID] [-s] [-t TM] [-I TO] " + "[-u U_A] [-v]\n" + " [-V] [-w WPR] DEVICE\n" ); pr2serr("\nUse '-h' or '--help' for more help\n"); return; @@ -274,9 +285,7 @@ usage(int do_help) " |-o OFF[,DLEN] (def: 0), then read DLEN bytes(def: " "rest of IF)\n" " --or|-O send ORWRITE command\n" - " --raw|-r read --scat_file=SF as binary (def: " - "ASCII hex)\n" - " --ref-tag=RT|-R RT expected reference tag field (def: " + " --ref-tag=RT|-r RT expected reference tag field (def: " "0xffffffff)\n" " --same=NDOB|-M NDOB send WRITE SAME command. NDOB (no " "data out buffer)\n" @@ -284,6 +293,8 @@ usage(int do_help) "1 (don't)\n" " --scat-file=SF|-q SF file containing LBA, NUM pairs, " "see manpage\n" + " --scat-raw|-R read --scat_file=SF as binary (def: " + "ASCII hex)\n" " --scattered=RD|-S RD send WRITE SCATTERED command with " "RD range\n" " descriptors (RD can be 0 when " @@ -376,7 +387,7 @@ usage(int do_help) " [--combined=DOF] [--dpo] [--fua] [--grpnum=GN]\n" " [--lba=LBA,LBA...] [--num=NUM,NUM...] " "[--offset=OFF[,DLEN]]\n" - " [--raw] [--ref-tag=RT] [--scat-file=SF] " + " [--ref-tag=RT] [--scat-file=SF] [--scat-raw] " "[--strict]\n" " [--tag-mask=TM] [--timeout=TO] [--wrprotect=WRP] " "DEVICE\n" @@ -385,10 +396,11 @@ usage(int do_help) " sg_write_x --scattered --in=IF [--bs=LBS] [--combined=DOF] " "[--dld=DLD]\n" " [--dpo] [--fua] [--grpnum=GN] [--lba=LBA,LBA...]\n" - " [--num=NUM,NUM...] [--offset=OFF[,DLEN]] [--raw] " - "[--scat-file=SF]\n" - " [--strict] [--timeout=TO] [--wrprotect=WRP] " - "DEVICE\n" + " [--num=NUM,NUM...] [--offset=OFF[,DLEN]] " + "[--scat-raw]\n" + " [--scat-file=SF] [--strict] [--timeout=TO] " + "[--wrprotect=WRP]\n" + " DEVICE\n" "\n" "WRITE STREAM (32) applicable options:\n" " sg_write_x --stream=ID --in=IF --32 [--app-tag=AT] " @@ -417,10 +429,10 @@ usage(int do_help) "and/or the\n" " --dry-run option\n" " - all WRITE X commands will accept --scat-file=SF and " - "optionally --raw\n" + "optionally --scat-raw\n" " options but only the first addr,num pair is used (any " "more are ignored)\n" - " - when '--raw --scat-file=SF' are used then the binary " + " - when '--rscat-aw --scat-file=SF' are used then the binary " "format expected in\n" " SF is as defined for the WRITE SCATTERED commands. " "That is 32 bytes\n" @@ -456,7 +468,7 @@ usage(int do_help) /* Returns true if num_of_f_chars of ASCII 'f' or 'F' characters are found * in sequence. Any leading "0x" or "0X" is ignored; otherwise false is - * returned (and the comparsion stops when the first mismatch is found). + * returned (and the comparison stops when the first mismatch is found). * For example a sequence of 'f' characters in a null terminated C string * that is two characters shorter than the requested num_of_f_chars will * compare the null character in the string with 'f', find them unequal, @@ -601,13 +613,13 @@ build_num_arr(const char * inp, uint32_t * num_arr, uint32_t * num_arr_len, * SG_LIB_SYNTAX_ERROR). If protection information fields not given, then * default values are given (i.e. all 0xff bytes). Ignores all spaces and * tabs and everything after '#' on lcp (assumed to be an ASCII line that - * is null terminated. If successful writes a LBA range descriptor starting - * at 'up'. */ + * is null terminated). If successful writes a LBA range descriptor starting + * at 'up'. The array starting at 'up' should be at least 20 bytes long. */ static int parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num) { bool ok; - int n; + int k; int64_t ll; const char * cp; const char * bp; @@ -615,13 +627,12 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num) bp = c; cp = strchr(lcp, '#'); - n = strspn(lcp, " \t"); - lcp = lcp + n; + lcp += strspn(lcp, " \t"); if (('\0' == *lcp) || (cp && (lcp >= cp))) return 999; /* blank line or blank prior to first '#' */ if (cp) { /* copy from first non whitespace ... */ - memcpy(c, lcp, cp - lcp); - c[cp - lcp] = '\0'; /* ... to just before first '#' */ + memcpy(c, lcp, cp - lcp); /* ... to just prior to first '#' */ + c[cp - lcp] = '\0'; } else strcpy(c, lcp); /* ... to end of line, including null */ ll = sg_get_llnum(bp); @@ -635,7 +646,7 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num) cp = strchr(bp, ','); if (cp) { bp = cp + 1; - if (*cp) { + if (*bp) { ll = sg_get_llnum(bp); if (-1 != ll) ok = true; @@ -647,64 +658,92 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num) } sg_put_unaligned_be32((uint32_t)ll, up + 8); *sum_num += (uint32_t)ll; - cp = strchr(bp, ','); - if (NULL == cp) { - sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12); - sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16); - sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18); - return 0; - } - ok = false; - bp = cp + 1; - if (*cp) { - ll = sg_get_llnum(bp); - if (-1 != ll) - ok = true; - } - if ((! ok) || (ll > UINT32_MAX)) { - pr2serr("%s: error reading RT (third) item on ", __func__); - return SG_LIB_SYNTAX_ERROR; - } - sg_put_unaligned_be32((uint32_t)ll, up + 12); - ok = false; - cp = strchr(bp, ','); - if (cp) { + /* now for 3 PI items */ + for (k = 0; k < 3; ++k) { + ok = true; + cp = strchr(bp, ','); + if (NULL == cp) + break; bp = cp + 1; - if (*cp) { - n = sg_get_num(bp); - if (-1 != ll) - ok = true; + if (*bp) { + cp += strspn(bp, " \t"); + if ('\0' == *cp) + break; + else if (',' == *cp) { + if (0 == k) + ll = DEF_RT; + else + ll = DEF_AT; /* DEF_AT and DEF_TM have same value */ + } else { + ll = sg_get_llnum(bp); + if (-1 == ll) + ok = false; + } } - } - if ((! ok) || (n > UINT16_MAX)) { - pr2serr("%s: error reading AT (fourth) item on ", __func__); - return SG_LIB_SYNTAX_ERROR; - } - sg_put_unaligned_be32((uint16_t)n, up + 16); - ok = false; - cp = strchr(bp, ','); - if (cp) { - bp = cp + 1; - if (*cp) { - n = sg_get_num(bp); - if (-1 != ll) - ok = true; + if (! ok) { + pr2serr("%s: error reading item %d NUM item on ", __func__, + k + 3); + break; } + switch (k) { + case 0: + if (ll > UINT32_MAX) { + pr2serr("%s: error with item 3, >0xffffffff; on ", __func__); + ok = false; + } else + sg_put_unaligned_be32((uint32_t)ll, up + 12); + break; + case 1: + if (ll > UINT16_MAX) { + pr2serr("%s: error with item 4, >0xffff; on ", __func__); + ok = false; + } else + sg_put_unaligned_be16((uint16_t)ll, up + 16); + break; + case 2: + if (ll > UINT16_MAX) { + pr2serr("%s: error with item 5, >0xffff; on ", __func__); + ok = false; + } else + sg_put_unaligned_be16((uint16_t)ll, up + 18); + break; + default: + pr2serr("%s: k=%d should not be >= 3\n", __func__, k); + ok = false; + break; + } + if (! ok) + break; } - if ((! ok) || (n > UINT16_MAX)) { - pr2serr("%s: error reading TM (fifth) item on ", __func__); + if (! ok) return SG_LIB_SYNTAX_ERROR; + for ( ; k < 3; ++k) { + switch (k) { + case 0: + sg_put_unaligned_be32((uint32_t)DEF_RT, up + 12); + break; + case 1: + sg_put_unaligned_be16((uint16_t)DEF_AT, up + 16); + break; + case 2: + sg_put_unaligned_be16((uint16_t)DEF_TM, up + 18); + break; + default: + pr2serr("%s: k=%d should not be >= 3\n", __func__, k); + ok = false; + break; + } } - return 0; + return ok ? 0 : SG_LIB_SYNTAX_ERROR; } /* Read pairs or LBAs and NUMs from a scat_file. A T10 scatter list array is * built at t10_scat_list_out (e.g. as per T10 the first 32 bytes are zeros * followed by the first LBA range descriptor (also 32 bytes long) then the - * second LBA range descriptor, etc. If pi_as_well is false then only LBA,NUM + * second LBA range descriptor, etc. If do_16 is true then only LBA,NUM * pairs are expected, loosely formatted if they are in the scat_file (e.g. * single line entries alternating LBA and NUM, with an even number of - * elements. If pa_as_well is true then a stricter format for quintets is + * elements. If do_16 is false then a stricter format for quintets is * expected: on each non comment line should contain: LBA,NUM[,RT,AT,TM] . If * RT,AT,TM are not given then they assume their defaults (i.e. 0xffffffff, * 0xffff, 0xffff). Each number (up to 64 bits in size) from command line or @@ -714,7 +753,7 @@ parse_scat_pi_line(const char * lcp, uint8_t * up, uint32_t * sum_num) * actual byte length of t10_scat_list_out written into act_list_blen and the * number of LBA range descriptors written in num_scat_elems . */ static int -build_t10_scat(const char * scat_fname, bool pi_as_well, +build_t10_scat(const char * scat_fname, bool do_16, uint8_t * t10_scat_list_out, uint32_t * act_list_blen, uint32_t * num_scat_elems, uint32_t * sum_num, int max_list_blen) @@ -776,7 +815,7 @@ build_t10_scat(const char * scat_fname, bool pi_as_well, __func__, scat_fname, j + 1, m + k + 1); goto bad_exit; } - if (pi_as_well) { + if (! do_16) { res = parse_scat_pi_line(lcp, up + n, sum_num); if (999 == res) ; @@ -829,7 +868,7 @@ build_t10_scat(const char * scat_fname, bool pi_as_well, } /* inner for loop(k) over line elements */ off += (k + 1); } /* outer for loop(j) over lines */ - if ((! pi_as_well) && (0x1 & off)) { + if (do_16 && (0x1 & off)) { pr2serr("%s: expect LBA,NUM pairs but decoded odd number\n from " "%s\n", __func__, scat_fname); goto bad_exit; @@ -852,6 +891,99 @@ is_pi_default(const struct opts_t * op) (DEF_TM == op->tag_mask)); } +/* Given a t10 parameter list header (32 zero bytes) for WRITE SCATTERED + * (16 or 32) followed by n RDs with a total length of at least + * max_lbrds_blen bytes, find "n" and increment where num_lbrds points + * n times. Further get the LBA length component from each RD and add each + * length into where sum_num points. Note: the caller probably wants to zero + * where num_lbrds and sum_num point before invoking this function. If all + * goes well return true, else false. If a degenerate RD is detected then + * if 'RD' (from --scattered=RD) is 0 then stop looking for further RDs; + * otherwise keep going. Currently overlapping LBA range descriptors are no + * checked for. If op->strict > 0 then the first 32 bytes are checked for + * zeros; any non-zero bytes will report to stderr, stop the check and + * return false. If op->strict > 0 then the trailing 20 or 12 bytes (only + * 12 if RT, AT and TM fields (for PI) are present) are checked for zeros; + * any non-zero bytes cause the same action as the previous check. If + * the number of RDs (when 'RD' from --scattered=RD > 0) is greater than + * the number of RDs found then a report is sent to stderr and if op->strict + * > 0 then returns false, else returns true. */ +static bool +check_lbrds(const uint8_t * up, int max_lbrds_blen, const struct opts_t * op, + uint32_t * num_lbrds, uint32_t * sum_num) +{ + bool ok; + int k, j, n; + const int max_lbrd_start = max_lbrds_blen - 32; + int vb = op->verbose; + + if (op->strict) { + if (max_lbrds_blen < 32) { + pr2serr("%s: logical block range descriptors too short " + "(%d < 32)\n", __func__, max_lbrds_blen); + return false; + } + if (! sg_all_zeros(up, 32)) { + pr2serr("%s: first 32 bytes of WRITE SCATTERED data-out buffer " + "should be zero.\nFound non-zero byte.\n", __func__); + return false; + } + } + if (max_lbrds_blen < 64) { + *num_lbrds = 0; + return true; + } + n = op->scat_num_lbrd ? -1 : (int)op->scat_num_lbrd; + for (k = 32, j = 0; k < max_lbrd_start; k += 32) { + if ((n < 0) && sg_all_zeros(up + k + 0, 12)) { /* degenerate LBA */ + if (vb) /* ... range descriptor terminator if --scattered=0 */ + pr2serr("%s: degenerate LBA range descriptor stops scan at " + "k=%d (RD=0)\n", __func__, k); + break; + } + *sum_num += sg_get_unaligned_be32(up + k + 8); + *num_lbrds += 1; + if (op->strict) { + ok = true; + if (op->wrprotect) { + if (! sg_all_zeros(up + k + 20, 12)) + ok = false; + } else if (! sg_all_zeros(up + k + 12, 20)) + ok = false; + if (! ok) { + pr2serr("%s: LB range descriptor %d non zero in reserved " + "fields\n", __func__, (k / 32) - 1); + return false; + } + } + ++j; + if (n >= 0) { + if (--n <= 0) + break; + } + } + if ((k < max_lbrd_start) && op->strict) { /* check pad all zeros */ + k += 32; + n = max_lbrds_blen - k; + if (! sg_all_zeros(up + k, n)) { + pr2serr("%s: pad (%d bytes) following LB range descriptors is " + "non zero\n", __func__, n); + return false; + } + } + if (vb > 2) + pr2serr("%s: about to return true, num_lbrds=%u, sum_num=%u " + "[k=%d, n=%d]\n", __func__, *num_lbrds, *sum_num, k, n); + if (n > 0) { + pr2serr("%s: number of range descriptors found (%d) less than RD " + "(%u) given to --scattered=\n", __func__, j, + op->scat_num_lbrd); + if (op->strict) + return false; + } + return true; +} + static int do_write_x(int sg_fd, const void * dataoutp, int dout_len, const struct opts_t * op) @@ -996,7 +1128,7 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len, x_cdb[2] |= 0x1; } sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 4); - sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 8); + sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 8); sg_put_unaligned_be32(op->numblocks, x_cdb + 10); } else { @@ -1007,8 +1139,9 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len, if (op->fua) x_cdb[10] |= 0x8; sg_put_unaligned_be16(op->scat_lbdof, x_cdb + 12); - sg_put_unaligned_be16(op->scat_num_lbard, x_cdb + 16); + sg_put_unaligned_be16(op->scat_num_lbrd, x_cdb + 16); sg_put_unaligned_be32(op->numblocks, x_cdb + 28); + /* ref_tag, app_tag and tag_mask placed in scatter list */ } } else if (op->do_stream) { if (16 == cdb_len) { @@ -1044,6 +1177,29 @@ do_write_x(int sg_fd, const void * dataoutp, int dout_len, pr2serr("%02x ", x_cdb[k]); pr2serr("\n"); } + if (op->do_scattered && (op->verbose > 2) && (dout_len > 63)) { + uint32_t sod_off = op->bs_pi_do * op->scat_lbdof; + const uint8_t * up = dataoutp; + + pr2serr(" %s scatter list, number of LBA range descriptors: %u\n", + op->cdb_name, op->scat_num_lbrd); + pr2serr(" byte offset of data_to_write: %u, dout_len: %d\n", + sod_off, dout_len); + up += 32; /* step over parameter list header */ + for (k = 0; k < (int)op->scat_num_lbrd; ++k, up += 32) { + pr2serr(" desc %d: LBA=0x%" PRIx64 " numblocks=%" PRIu32 + "%s", k, sg_get_unaligned_be64(up + 0), + sg_get_unaligned_be32(up + 8), (op->do_16 ? "\n" : " ")); + if (! op->do_16) + pr2serr("rt=0x%x at=0x%x tm=0x%x\n", + sg_get_unaligned_be32(up + 12), + sg_get_unaligned_be16(up + 16), + sg_get_unaligned_be16(up + 18)); + if ((uint32_t)(((k + 2) * 32) + 20) > sod_off) + pr2serr("Warning: possible clash of descriptor %u with " + "data_to_write\n", k); + } + } if ((op->verbose > 3) && (dout_len > 0)) { pr2serr(" Data-out buffer contents:\n"); dStrHexErr((const char *)dataoutp, op->xfer_bytes, 1); @@ -1205,7 +1361,7 @@ do_read_capacity(int sg_fd, struct opts_t *op) #define WANT_ZERO_EXIT 9999 static const char * const opt_long_ctl_str = - "36a:A:b:B:c:dD:Efg:G:hi:I:l:M:n:No:Oq:rR:sS:t:T:u:vVw:x"; + "36a:A:b:B:c:dD:Efg:G:hi:I:l:M:n:No:Oq:r:RsS:t:T:u:vVw:x"; /* command line processing, options and arguments. Returns 0 if ok, * returns WANT_ZERO_EXIT so upper level yields an exist status of zero. @@ -1379,14 +1535,19 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp, pr2serr("bad first argument to '--offset='\n"); return SG_LIB_SYNTAX_ERROR; } - op->offset = (uint64_t)ll; + op->if_offset = (uint64_t)ll; if ((cp = strchr(optarg, ','))) { ll = sg_get_llnum(cp + 1); if (-1 == ll) { pr2serr("bad second argument to '--offset='\n"); return SG_LIB_SYNTAX_ERROR; } - op->dlen = (uint64_t)ll; + if (ll > UINT32_MAX) { + pr2serr("bad second argument to '--offset=', cannot " + "exceed 32 bits\n"); + return SG_LIB_SYNTAX_ERROR; + } + op->if_dlen = (uint32_t)ll; } break; case 'O': @@ -1396,7 +1557,10 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp, case 'q': op->scat_filename = optarg; break; - case 'r': + case 'R': + op->do_scat_raw = true; + break; + case 'r': /* same as --ref-tag= */ ll = sg_get_llnum(optarg); if ((ll < 0) || (ll > UINT32_MAX)) { pr2serr("bad argument to '--ref-tag='. Expect 0 to " @@ -1405,15 +1569,6 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp, } op->ref_tag = (uint32_t)ll; break; - case 'R': /* same as --ref-tag= */ - ll = sg_get_llnum(optarg); - if ((ll < 0) || (ll > UINT32_MAX)) { - pr2serr("bad argument to '--ref-tag='. Expect 0 to 0xffffffff " - "inclusive\n"); - return SG_LIB_SYNTAX_ERROR; - } - op->ref_tag = (uint32_t)ll; - break; case 's': op->strict = true; break; @@ -1424,7 +1579,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp, "inclusive\n"); return SG_LIB_SYNTAX_ERROR; } - op->scat_num_lbard = (uint16_t)j; + op->scat_num_lbrd = (uint16_t)j; op->do_scattered = true; op->cmd_name = "Write scattered"; break; @@ -1461,7 +1616,7 @@ cl_process(struct opts_t *op, int argc, char *argv[], const char ** lba_opp, ++op->verbose; break; case 'V': - pr2serr(ME "version: %s\n", version_str); + pr2serr("sg_write_x version: %s\n", version_str); return WANT_ZERO_EXIT; case 'w': /* WRPROTECT field (or ORPROTECT for ORWRITE) */ op->wrprotect = sg_get_num(optarg); @@ -1506,9 +1661,11 @@ main(int argc, char * argv[]) int sg_fd = -1; int rsl_fd = -1; int ret = -1; - uint32_t addr_arr_len, num_arr_len, num_lbard, do_len; + uint32_t addr_arr_len, num_arr_len, do_len, s; + uint32_t num_lbrd = 0; + uint32_t if_len = 0; ssize_t res; - off_t if_len = 0; + off_t if_tot_len = 0; struct opts_t * op; unsigned char * wBuff = NULL; const char * lba_op = NULL; @@ -1528,10 +1685,11 @@ main(int argc, char * argv[]) op->numblocks = DEF_WR_NUMBLOCKS; op->pi_type = -1; /* Protection information type unknown */ op->ref_tag = DEF_RT; /* first 4 bytes of 8 byte protection info */ - op->app_tag = DEF_AT; /* part of protection information */ - op->tag_mask = DEF_TM; /* part of protection information */ + op->app_tag = DEF_AT; /* 2 bytes of protection information */ + op->tag_mask = DEF_TM; /* final 2 bytes of protection information */ op->timeout = DEF_TIMEOUT_SECS; + /* Process command line */ ret = cl_process(op, argc, argv, &lba_op, &num_op); if (ret) { if (WANT_ZERO_EXIT == ret) @@ -1543,6 +1701,7 @@ main(int argc, char * argv[]) return 0; } vb = op->verbose; + /* sanity checks */ if ((! op->do_16) && (! op->do_32)) { op->do_16 = true; if (vb > 1) @@ -1566,8 +1725,23 @@ main(int argc, char * argv[]) } snprintf(op->cdb_name, sizeof(op->cdb_name), "%s(%d)", op->cmd_name, (op->do_16 ? 16 : 32)); + if (op->do_combined) { + if (! op->do_scattered) { + pr2serr("--combined=DOF only allowed with --scattered=RD (i.e. " + "only with\nWRITE SCATTERED command)\n"); + return SG_LIB_SYNTAX_ERROR; + } + if (lba_op || num_op) { + pr2serr("--scattered=RD --combined=DOF does not use --lba= or " + "--num=\nPlease remove.\n"); + return SG_LIB_SYNTAX_ERROR; + } + } + /* examine .if_name . Open, move to .if_offset, calculate length that we + * want to read. */ if (! op->ndob) { + if_len = op->if_dlen; if (NULL == op->if_name) { pr2serr("Need --if=FN option to be given, exiting.\n"); if (vb > 1) @@ -1599,16 +1773,17 @@ main(int argc, char * argv[]) } got_stat = true; if (S_ISREG(if_stat.st_mode)) - if_len = if_stat.st_size; + if_tot_len = if_stat.st_size; } - if (got_stat && if_len && ((int64_t)op->offset >= (if_len - 1))) { + if (got_stat && if_tot_len && + ((int64_t)op->if_offset >= (if_tot_len - 1))) { pr2serr("Offset (%" PRIu64 ") is at or beyond IF byte length (%" - PRIu64 ")\n", op->offset, if_len); + PRIu64 ")\n", op->if_offset, if_tot_len); ret = SG_LIB_FILE_ERROR; goto err_out; } - if (op->offset > 0) { - off_t off = op->offset; + if (op->if_offset > 0) { + off_t off = op->if_offset; if (got_stdin) { if (vb) @@ -1623,17 +1798,27 @@ main(int argc, char * argv[]) ret = SG_LIB_FILE_ERROR; goto err_out; } - if_len -= op->offset; - if (if_len <= 0) { + if_tot_len -= op->if_offset; + if (if_tot_len <= 0) { pr2serr("--offset [0x%" PRIx64 "] at or beyond file " - "length[0x%" PRIx64 "]\n", (uint64_t)op->offset, - (uint64_t)if_len); + "length[0x%" PRIx64 "]\n", + (uint64_t)op->if_offset, (uint64_t)if_tot_len); ret = SG_LIB_FILE_ERROR; goto err_out; } + if_len = (uint32_t)((if_tot_len < (off_t)op->if_dlen) ? + if_tot_len : (off_t)op->if_dlen); } } if (0 != (if_len % op->bs_pi_do)) { + if (op->strict) { + pr2serr("Error: number of bytes to read from IF [%u] is " + "not a multiple\nblock size %u (including" + "protection information\n", (unsigned int)if_len, + op->bs_pi_do); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } pr2serr("Warning: number of bytes to read from IF [%u] is not a " "multiple\nblock size %u (including protection " "information, if any);\npad with zeros", @@ -1642,32 +1827,26 @@ main(int argc, char * argv[]) op->bs_pi_do; /* round up */ } } + /* A bit more sanity */ if (NULL == op->device_name) { pr2serr("missing device name!\n"); usage((op->help > 0) ? op->help : 0); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } - if (op->scat_filename && (lba_op || num_op)) { - pr2serr("expect '--scat-file=' by itself, or both '--lba=' and " - "'--num='\n"); - ret = SG_LIB_SYNTAX_ERROR; - goto err_out; - } else if (op->scat_filename || (lba_op && num_op)) - ; /* we want this path */ - else { - if (lba_op) - pr2serr("since '--lba=' is given, also need '--num='\n"); - else - pr2serr("expect either both '--lba=' and '--num=', or " - "'--scat-file=' by itself\n"); + n = (!! op->scat_filename) + (!! (lba_op || num_op)) + + (!! op->do_combined); + if (1 != n) { + pr2serr("want one and only one of: (--lba=LBA or --num=NUM), or " + "--scat-file=SF,\nor --combined=DOF\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } + /* Open device file, do READ CAPACITY(16, maybe 10) if no BS */ sg_fd = sg_cmds_open_device(op->device_name, false /* rw */, vb); if (sg_fd < 0) { - pr2serr(ME "open error: %s: %s\n", op->device_name, + pr2serr("open error: %s: %s\n", op->device_name, safe_strerror(-sg_fd)); return SG_LIB_FILE_ERROR; } @@ -1677,6 +1856,7 @@ main(int argc, char * argv[]) goto err_out; } + /* decode --lba= and --num= options */ memset(addr_arr, 0, sizeof(addr_arr)); memset(num_arr, 0, sizeof(num_arr)); addr_arr_len = 0; @@ -1687,6 +1867,8 @@ main(int argc, char * argv[]) ret = SG_LIB_SYNTAX_ERROR; goto err_out; } + if (addr_arr_len > 0) + op->explicit_lba = true; if (0 != build_num_arr(num_op, num_arr, &num_arr_len, MAX_NUM_ADDR)) { pr2serr("bad argument to '--num'\n"); @@ -1705,6 +1887,7 @@ main(int argc, char * argv[]) uint32_t sum_num = 0; do_len = 0; + /* if WRITE SCATTERED check for --scat-file=SF, if so state(SF) */ if (op->scat_filename) { if (op->do_combined) { pr2serr("Ambiguous: got --combined=DOF and --scat-file=SF " @@ -1720,31 +1903,34 @@ main(int argc, char * argv[]) goto err_out; } } - if (op->do_combined && op->do_raw) { - pr2serr("Ambiguous: do expect --combined=DOF and --raw\n" + /* some WRITE SCATTERED sanity checks */ + if (op->do_combined && op->do_scat_raw) { + pr2serr("Ambiguous: do expect --combined=DOF and --scat-raw\n" "Give one or the other\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } - if ((NULL == op->scat_filename) && op->do_raw) { - pr2serr("--raw only applies to the --scat-file=SF option\n" - "Give both or neither\n"); + if ((NULL == op->scat_filename) && op->do_scat_raw) { + pr2serr("--scat-raw only applies to the --scat-file=SF option\n" + "--scat-raw without the --scat-file=SF option is an " + "error\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } - if ((addr_arr_len > 0) && (op->scat_num_lbard > 0) && - (op->scat_num_lbard < addr_arr_len)) { + if ((addr_arr_len > 0) && (op->scat_num_lbrd > 0) && + (op->scat_num_lbrd < addr_arr_len)) { pr2serr("less LBA,NUM pairs (%d )than --scattered=%d\n", - addr_arr_len, op->scat_num_lbard); + addr_arr_len, op->scat_num_lbrd); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } - num_lbard = (addr_arr_len > 0) ? addr_arr_len : op->scat_num_lbard; - if (num_lbard < 15) - num_lbard = 15; /* 32 byte leadin, 15 32 byte LRD = 512 bytes */ - if (op->do_combined) + num_lbrd = (addr_arr_len > 0) ? addr_arr_len : op->scat_num_lbrd; + if (num_lbrd < 15) + num_lbrd = 15; /* 32 byte leadin, 15 32 byte LRD = 512 bytes */ + if (op->do_combined) { goto skip_scat_build; - if (op->do_raw) { + } + if (op->do_scat_raw) { if (S_ISREG(sf_stat.st_mode)) { do_len = sf_stat.st_size; d = sf_stat.st_size / 32; @@ -1756,13 +1942,13 @@ main(int argc, char * argv[]) } if (sf_stat.st_size % 32) d += 1; /* round up, will zero pad unfinished RD */ - if (op->scat_num_lbard) { - if (op->scat_num_lbard != (d - 1)) { + if (op->scat_num_lbrd) { + if (op->scat_num_lbrd != (d - 1)) { pr2serr("Command line RD (%u) contradicts value " "calculated from raw SF (%u)\n", - op->scat_num_lbard, d - 1); - if (op->scat_num_lbard < (d - 1)) - d = op->scat_num_lbard + 1; + op->scat_num_lbrd, d - 1); + if (op->scat_num_lbrd < (d - 1)) + d = op->scat_num_lbrd + 1; else { pr2serr("Command line RD greater than raw SF " "file length implies, exit\n"); @@ -1772,14 +1958,16 @@ main(int argc, char * argv[]) } } } else { - pr2serr("--scat-file= --raw wants regular file for length\n"); + pr2serr("--scat-file= --scat-raw wants regular file for " + "length\n"); ret = SG_LIB_FILE_ERROR; goto err_out; } - num_lbard = d; + num_lbrd = d; } - do_len = (1 + num_lbard) * 32; + /* Calculations to work out initial dout length */ + do_len = (1 + num_lbrd) * 32; op->scat_lbdof = do_len / op->bs_pi_do; if (0 != (do_len % op->bs_pi_do)) { /* if not multiple, round up */ op->scat_lbdof += 1; @@ -1789,13 +1977,13 @@ main(int argc, char * argv[]) do_len += (uint32_t)if_len; } else { /* IF is stdin, a pipe or a device (special) ... */ op->xfer_bytes = _SC_PAGE_SIZE; /* ... so need length */ - if (op->bs_pi_do > (op->xfer_bytes / 2)) + if (op->bs_pi_do > ((uint32_t)op->xfer_bytes / 2)) op->xfer_bytes = op->bs_pi_do * 3; - else if (do_len >= (op->xfer_bytes / 2)) { + else if (do_len >= ((uint32_t)op->xfer_bytes / 2)) { op->xfer_bytes *= 4; - if (do_len >= (op->xfer_bytes / 2)) { + if (do_len >= ((uint32_t)op->xfer_bytes / 2)) { op->xfer_bytes *= 4; - if (do_len >= (op->xfer_bytes / 2)) { + if (do_len >= ((uint32_t)op->xfer_bytes / 2)) { pr2serr("Giving up guessing big enough buffers, " "please use --offset=OFF,DLEN\n"); ret = SG_LIB_SYNTAX_ERROR; @@ -1805,8 +1993,8 @@ main(int argc, char * argv[]) } do_len = op->xfer_bytes; } - if (0 != (do_len % op->bs_pi_do)) /* round up */ - do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do; + if (0 != (do_len % op->bs_pi_do)) /* round up */ + do_len = ((do_len / op->bs_pi_do) + 1) * op->bs_pi_do; if (do_len < op->bs_pi_do) { pr2serr("failed calculating data-out buffer size (%u)\n", do_len); @@ -1825,7 +2013,8 @@ main(int argc, char * argv[]) ret = SG_LIB_OS_BASE_ERR + ENOMEM; goto err_out; } - if (op->do_raw) { + + if (op->do_scat_raw) { rsl_fd = open(op->scat_filename, O_RDONLY); if (rsl_fd < 0) { err = errno; @@ -1861,12 +2050,17 @@ main(int argc, char * argv[]) close(rsl_fd); rsl_fd = -1; } else if (op->scat_filename) { - ret = build_t10_scat(op->scat_filename, op->expect_pi_do, up, &d, - &num_lbard, &sum_num, + ret = build_t10_scat(op->scat_filename, op->do_16, up, &d, + &num_lbrd, &sum_num, op->scat_lbdof * op->bs_pi_do); if (ret) goto err_out; + if (num_lbrd > 0) + op->explicit_lba = true; op->numblocks = sum_num; + if (vb > 1) + pr2serr("After build_t10_scat(): num_lbrd=%u sum_num=%u\n", + num_lbrd, sum_num); } else if (addr_arr_len > 0) { /* build RDs for --addr= --num= */ for (n = 32, k = 0; k < (int)addr_arr_len; ++k, n += 32) { sg_put_unaligned_be64(addr_arr[k], up + n + 0); @@ -1886,14 +2080,30 @@ main(int argc, char * argv[]) } op->numblocks = sum_num; } - /* now read data to write component into up */ + /* now read data to write component into 'up' */ d = op->scat_lbdof * op->bs_pi_do; - if (d > op->xfer_bytes) { + if (d > (uint32_t)op->xfer_bytes) { pr2serr("Logic error in scattered, read data into buffer " "(d=%u)\n", d); ret = SG_LIB_FILE_ERROR; goto err_out; } + s = op->scat_lbdof + op->numblocks; + if ((uint32_t)op->xfer_bytes < (s * op->bs_pi_do)) { + uint8_t * u2p; + + u2p = calloc(s, op->bs_pi_do); + if (NULL == u2p) { + pr2serr("unable to allocate memory for final " + "scatterlist+data\n"); + ret = SG_LIB_OS_BASE_ERR + ENOMEM; + goto err_out; + } + memcpy(u2p, up, d); + free(up); + up = u2p; + op->xfer_bytes = s * op->bs_pi_do; + } res = read(infd, up + d, op->xfer_bytes - d); d = op->xfer_bytes - d; if (res < 0) { @@ -1902,7 +2112,7 @@ main(int argc, char * argv[]) ret = SG_LIB_FILE_ERROR; goto err_out; } - if (res < d) { + if ((uint32_t)res < d) { pr2serr("Short (%u) read of IF file, wanted %u\n", (unsigned int)res, d); ret = SG_LIB_FILE_ERROR; @@ -1919,7 +2129,35 @@ skip_scat_build: // needs more XXXXXXXXX xxxxxx ??? if (op->do_same) op->xfer_bytes = 1 * op->bs_pi_do; else if (op->do_scattered) { - ; /* already done, scatter_list+data waiting in 'up' */ + if (op->do_combined) { + int up_len; + uint32_t sum_num; + + if ((if_len < 32) || (op->bs_pi_do < 32)) { + pr2serr("Logic error combined calloc should be > %u, " + "bs_pi_do=%u\n", (uint32_t)if_len, op->bs_pi_do); + ret = SG_LIB_FILE_ERROR; + goto err_out; + } + /* assume if_len % op->bs_pi_do is zero (i.e. no remainder) */ + up = calloc(if_len / op->bs_pi_do, op->bs_pi_do); + if (NULL == up) { + pr2serr("unable to allocate memory for combined\n"); + ret = SG_LIB_OS_BASE_ERR + ENOMEM; + goto err_out; + } + up_len = (op->scat_lbdof > 0) ? (op->scat_lbdof * op->bs_pi_do) : + if_len; + num_lbrd = 0; + sum_num = 0; + if (! check_lbrds(up, up_len, op, &num_lbrd, &sum_num)) { + ret = SG_LIB_FILE_ERROR; + goto err_out; + } + op->numblocks = sum_num; + } else { + ; /* already done, scatter_list+data waiting in 'up' */ + } } else op->xfer_bytes = op->numblocks * op->bs_pi_do; |