static int do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp, int * act_cdb_lenp) { int k, ret, res, sense_cat, cdb_len; uint64_t llba; uint8_t ws_cdb[WRITE_SAME32_LEN]; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; cdb_len = op->pref_cdb_size; if (WRITE_SAME10_LEN == cdb_len) { llba = op->lba + op->numblocks; if ((op->numblocks > 0xffff) || (llba > UINT32_MAX) || op->ndob || (op->unmap && (! op->want_ws10))) { cdb_len = WRITE_SAME16_LEN; if (op->verbose) { const char * cp = "use WRITE SAME(16) instead of 10 byte " "cdb"; if (op->numblocks > 0xffff) pr2serr("%s since blocks exceed 65535\n", cp); else if (llba > UINT32_MAX) pr2serr("%s since LBA may exceed 32 bits\n", cp); else pr2serr("%s due to ndob or unmap settings\n", cp); } } } if (act_cdb_lenp) *act_cdb_lenp = cdb_len; memset(ws_cdb, 0, sizeof(ws_cdb)); switch (cdb_len) { case WRITE_SAME10_LEN: ws_cdb[0] = WRITE_SAME10_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); /* ANCHOR + UNMAP not allowed for WRITE_SAME10 in sbc3r24+r25 but * a proposal has been made to allow it. Anticipate approval. */ if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; sg_put_unaligned_be32((uint32_t)op->lba, ws_cdb + 2); ws_cdb[6] = (op->grpnum & 0x1f); sg_put_unaligned_be16((uint16_t)op->numblocks, ws_cdb + 7); break; case WRITE_SAME16_LEN: ws_cdb[0] = WRITE_SAME16_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; if (op->ndob) ws_cdb[1] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 2); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 10); ws_cdb[14] = (op->grpnum & 0x1f); break; case WRITE_SAME32_LEN: ws_cdb[0] = VARIABLE_LEN_OP; ws_cdb[6] = (op->grpnum & 0x1f); ws_cdb[7] = WRITE_SAME32_ADD; sg_put_unaligned_be16((uint16_t)WRITE_SAME32_SA, ws_cdb + 8); ws_cdb[10] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[10] |= 0x10; if (op->unmap) ws_cdb[10] |= 0x8; if (op->pbdata) ws_cdb[10] |= 0x4; if (op->lbdata) ws_cdb[10] |= 0x2; if (op->ndob) ws_cdb[10] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 12); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 28); break; default: pr2serr("do_write_same: bad cdb length %d\n", cdb_len); return -1; } if (op->verbose > 1) { pr2serr(" Write same(%d) cdb: ", cdb_len); for (k = 0; k < cdb_len; ++k) pr2serr("%02x ", ws_cdb[k]); pr2serr("\n Data-out buffer length=%d\n", op->xfer_len); } if ((op->verbose > 3) && (op->xfer_len > 0)) { pr2serr(" Data-out buffer contents:\n"); hex2stderr((const uint8_t *)dataoutp, op->xfer_len, 1); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("Write same(%d): out of memory\n", cdb_len); return -1; } set_scsi_pt_cdb(ptvp, ws_cdb, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, op->xfer_len); res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose); ret = sg_cmds_process_resp(ptvp, "Write same", res, SG_NO_DATA_IN, sense_b, true /*noisy */, op->verbose, &sense_cat); if (-1 == ret) get_scsi_pt_os_err(ptvp); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { bool valid; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) pr2serr("Medium or hardware error starting at lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); } ret = sense_cat; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI WRITE AND VERIFY according with CDB. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int run_scsi_transaction(int sg_fd, const uint8_t *cdbp, int cdb_len, uint8_t *dop, int do_len, int timeout, bool noisy, int verbose) { int res, k, sense_cat, ret; struct sg_pt_base * ptvp; uint8_t sense_b[SENSE_BUFF_LEN]; char b[32]; snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len); if (verbose) { pr2serr(" %s cdb: ", b); for (k = 0; k < cdb_len; ++k) pr2serr("%02x ", cdbp[k]); pr2serr("\n"); if ((verbose > 2) && dop && do_len) { pr2serr(" Data out buffer [%d bytes]:\n", do_len); hex2stderr(dop, do_len, -1); } } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", b); return -1; } set_scsi_pt_cdb(ptvp, cdbp, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, dop, do_len); res = do_scsi_pt(ptvp, sg_fd, timeout, verbose); ret = sg_cmds_process_resp(ptvp, b, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: /* write or verify failed */ { bool valid; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) pr2serr("Medium or hardware error starting at lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); } ret = sense_cat; break; case SG_LIB_CAT_PROTECTION: /* PI failure */ case SG_LIB_CAT_MISCOMPARE: /* only in bytchk=1 case */ default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }