aboutsummaryrefslogtreecommitdiff
path: root/src/sg_write_same.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_write_same.c')
-rw-r--r--src/sg_write_same.c678
1 files changed, 678 insertions, 0 deletions
diff --git a/src/sg_write_same.c b/src/sg_write_same.c
new file mode 100644
index 00000000..bfdf8159
--- /dev/null
+++ b/src/sg_write_same.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2009-2022 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 <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sg_lib.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.34 20220127";
+
+
+#define ME "sg_write_same: "
+
+#define WRITE_SAME10_OP 0x41
+#define WRITE_SAME16_OP 0x93
+#define VARIABLE_LEN_OP 0x7f
+#define WRITE_SAME32_SA 0xd
+#define WRITE_SAME32_ADD 0x18
+#define WRITE_SAME10_LEN 10
+#define WRITE_SAME16_LEN 16
+#define WRITE_SAME32_LEN 32
+#define RCAP10_RESP_LEN 8
+#define RCAP16_RESP_LEN 32
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define DEF_TIMEOUT_SECS 60
+#define DEF_WS_CDB_SIZE WRITE_SAME10_LEN
+#define DEF_WS_NUMBLOCKS 1
+#define MAX_XFER_LEN (64 * 1024)
+#define EBUFF_SZ 512
+
+#ifndef UINT32_MAX
+#define UINT32_MAX ((uint32_t)-1)
+#endif
+
+static struct option long_options[] = {
+ {"10", no_argument, 0, 'R'},
+ {"16", no_argument, 0, 'S'},
+ {"32", no_argument, 0, 'T'},
+ {"anchor", no_argument, 0, 'a'},
+ {"ff", no_argument, 0, 'f'},
+ {"grpnum", required_argument, 0, 'g'},
+ {"help", no_argument, 0, 'h'},
+ {"in", required_argument, 0, 'i'},
+ {"lba", required_argument, 0, 'l'},
+ {"lbdata", no_argument, 0, 'L'},
+ {"ndob", no_argument, 0, 'N'},
+ {"num", required_argument, 0, 'n'},
+ {"pbdata", no_argument, 0, 'P'},
+ {"timeout", required_argument, 0, 't'},
+ {"unmap", no_argument, 0, 'U'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"wrprotect", required_argument, 0, 'w'},
+ {"xferlen", required_argument, 0, 'x'},
+ {0, 0, 0, 0},
+};
+
+struct opts_t {
+ bool anchor;
+ bool ff;
+ bool ndob;
+ bool lbdata;
+ bool pbdata;
+ bool unmap;
+ bool verbose_given;
+ bool version_given;
+ bool want_ws10;
+ int grpnum;
+ int numblocks;
+ int timeout;
+ int verbose;
+ int wrprotect;
+ int xfer_len;
+ int pref_cdb_size;
+ uint64_t lba;
+ char ifilename[256];
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_write_same [--10] [--16] [--32] [--anchor] "
+ "[-ff] [--grpnum=GN]\n"
+ " [--help] [--in=IF] [--lba=LBA] [--lbdata] "
+ "[--ndob]\n"
+ " [--num=NUM] [--pbdata] [--timeout=TO] "
+ "[--unmap]\n"
+ " [--verbose] [--version] [--wrprotect=WRP] "
+ "[xferlen=LEN]\n"
+ " DEVICE\n"
+ " where:\n"
+ " --10|-R send WRITE SAME(10) (even if '--unmap' "
+ "is given)\n"
+ " --16|-S send WRITE SAME(16) (def: 10 unless "
+ "'--unmap' given,\n"
+ " LBA+NUM > 32 bits, or NUM > 65535; "
+ "then def 16)\n"
+ " --32|-T send WRITE SAME(32) (def: 10 or 16)\n"
+ " --anchor|-a set ANCHOR field in cdb\n"
+ " --ff|-f use buffer of 0xff bytes for fill "
+ "(def: 0x0 bytes)\n"
+ " --grpnum=GN|-g GN GN is group number field (def: 0)\n"
+ " --help|-h print out usage message\n"
+ " --in=IF|-i IF IF is file to fetch one block of data "
+ "from (use LEN\n"
+ " bytes or whole file). Block written to "
+ "DEVICE\n"
+ " --lba=LBA|-l LBA LBA is the logical block address to "
+ "start (def: 0)\n"
+ " --lbdata|-L set LBDATA bit (obsolete)\n"
+ " --ndob|-N set NDOB (no data-out buffer) bit in "
+ "cdb\n"
+ " --num=NUM|-n NUM NUM is number of logical blocks to "
+ "write (def: 1)\n"
+ " [Beware NUM==0 may mean: 'rest of "
+ "device']\n"
+ " --pbdata|-P set PBDATA bit (obsolete)\n"
+ " --timeout=TO|-t TO command timeout (unit: seconds) (def: "
+ "60)\n"
+ " --unmap|-U set UNMAP bit\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string then exit\n"
+ " --wrprotect=WPR|-w WPR WPR is the WRPROTECT field value "
+ "(def: 0)\n"
+ " --xferlen=LEN|-x LEN LEN is number of bytes from IF to "
+ "send to\n"
+ " DEVICE (def: IF file length)\n\n"
+ "Performs a SCSI WRITE SAME (10, 16 or 32) command. NDOB bit is "
+ "only\nsupported by the 16 and 32 byte variants. When set the "
+ "specified blocks\nwill be filled with zeros or the "
+ "'provisioning initialization pattern'\nas indicated by the "
+ "LBPRZ field. As a precaution one of the '--in=',\n'--lba=' or "
+ "'--num=' options is required.\nAnother implementation of WRITE "
+ "SAME is found in the sg_write_x utility.\n"
+ );
+}
+
+static int
+do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp,
+ int * act_cdb_lenp)
+{
+ int ret, res, sense_cat, cdb_len;
+ uint64_t llba;
+ uint8_t ws_cdb[WRITE_SAME32_LEN] SG_C_CPP_ZERO_INIT;
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+ 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;
+ 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 & GRPNUM_MASK);
+ 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 & GRPNUM_MASK);
+ break;
+ case WRITE_SAME32_LEN:
+ ws_cdb[0] = VARIABLE_LEN_OP;
+ ws_cdb[6] = (op->grpnum & GRPNUM_MASK);
+ 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) {
+ char b[128];
+
+ pr2serr(" Write same(%d) cdb: %s\n", cdb_len,
+ sg_get_command_str(ws_cdb, cdb_len, false, sizeof(b), b));
+ pr2serr(" 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, true /*noisy */,
+ op->verbose, &sense_cat);
+ if (-1 == ret) {
+ if (get_scsi_pt_transport_err(ptvp))
+ ret = SG_LIB_TRANSPORT_ERROR;
+ else
+ 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:
+ {
+ 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_ILLEGAL_REQ:
+ if (op->verbose)
+ sg_print_command_len(ws_cdb, cdb_len);
+ /* FALL THROUGH */
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else
+ ret = 0;
+
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool got_stdin = false;
+ bool if_given = false;
+ bool lba_given = false;
+ bool num_given = false;
+ bool prot_en;
+ int res, c, infd, act_cdb_len, vb, err;
+ int sg_fd = -1;
+ int ret = -1;
+ uint32_t block_size;
+ int64_t ll;
+ const char * device_name = NULL;
+ struct opts_t * op;
+ uint8_t * wBuff = NULL;
+ uint8_t * free_wBuff = NULL;
+ char ebuff[EBUFF_SZ];
+ char b[80];
+ uint8_t resp_buff[RCAP16_RESP_LEN];
+ struct opts_t opts;
+ struct stat a_stat;
+
+ op = &opts;
+ memset(op, 0, sizeof(opts));
+ op->numblocks = DEF_WS_NUMBLOCKS;
+ op->pref_cdb_size = DEF_WS_CDB_SIZE;
+ op->timeout = DEF_TIMEOUT_SECS;
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "afg:hi:l:Ln:NPRSt:TUvVw:x:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ op->anchor = true;
+ break;
+ case 'f':
+ op->ff = true;
+ break;
+ case 'g':
+ op->grpnum = sg_get_num(optarg);
+ if ((op->grpnum < 0) || (op->grpnum > 63)) {
+ pr2serr("bad argument to '--grpnum'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'i':
+ strncpy(op->ifilename, optarg, sizeof(op->ifilename) - 1);
+ op->ifilename[sizeof(op->ifilename) - 1] = '\0';
+ if_given = true;
+ break;
+ case 'l':
+ ll = sg_get_llnum(optarg);
+ if (-1 == ll) {
+ pr2serr("bad argument to '--lba'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->lba = (uint64_t)ll;
+ lba_given = true;
+ break;
+ case 'L':
+ op->lbdata = true;
+ break;
+ case 'n':
+ op->numblocks = sg_get_num(optarg);
+ if (op->numblocks < 0) {
+ pr2serr("bad argument to '--num'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ num_given = true;
+ break;
+ case 'N':
+ op->ndob = true;
+ break;
+ case 'P':
+ op->pbdata = true;
+ break;
+ case 'R':
+ op->want_ws10 = true;
+ break;
+ case 'S':
+ if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
+ pr2serr("only one '--10', '--16' or '--32' please\n");
+ return SG_LIB_CONTRADICT;
+ }
+ op->pref_cdb_size = 16;
+ break;
+ case 't':
+ op->timeout = sg_get_num(optarg);
+ if (op->timeout < 0) {
+ pr2serr("bad argument to '--timeout'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'T':
+ if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
+ pr2serr("only one '--10', '--16' or '--32' please\n");
+ return SG_LIB_CONTRADICT;
+ }
+ op->pref_cdb_size = 32;
+ break;
+ case 'U':
+ op->unmap = true;
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->verbose;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ case 'w':
+ op->wrprotect = sg_get_num(optarg);
+ if ((op->wrprotect < 0) || (op->wrprotect > 7)) {
+ pr2serr("bad argument to '--wrprotect'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'x':
+ op->xfer_len = sg_get_num(optarg);
+ if (op->xfer_len < 0) {
+ pr2serr("bad argument to '--xferlen'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ 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 (op->want_ws10 && (DEF_WS_CDB_SIZE != op->pref_cdb_size)) {
+ pr2serr("only one '--10', '--16' or '--32' please\n");
+ return SG_LIB_CONTRADICT;
+ }
+
+#ifdef DEBUG
+ pr2serr("In DEBUG mode, ");
+ if (op->verbose_given && op->version_given) {
+ pr2serr("but override: '-vV' given, zero verbose and continue\n");
+ op->verbose_given = false;
+ op->version_given = false;
+ op->verbose = 0;
+ } else if (! op->verbose_given) {
+ pr2serr("set '-vv'\n");
+ op->verbose = 2;
+ } else
+ pr2serr("keep verbose=%d\n", op->verbose);
+#else
+ if (op->verbose_given && op->version_given)
+ pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
+#endif
+ if (op->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;
+ }
+ vb = op->verbose;
+
+ if ((! if_given) && (! lba_given) && (! num_given)) {
+ pr2serr("As a precaution, one of '--in=', '--lba=' or '--num=' is "
+ "required\n");
+ return SG_LIB_CONTRADICT;
+ }
+
+ if (op->ndob) {
+ if (if_given) {
+ pr2serr("Can't have both --ndob and '--in='\n");
+ return SG_LIB_CONTRADICT;
+ }
+ if (0 != op->xfer_len) {
+ pr2serr("With --ndob only '--xferlen=0' (or not given) is "
+ "acceptable\n");
+ return SG_LIB_CONTRADICT;
+ }
+ } else if (op->ifilename[0]) {
+ got_stdin = (0 == strcmp(op->ifilename, "-"));
+ if (! got_stdin) {
+ memset(&a_stat, 0, sizeof(a_stat));
+ if (stat(op->ifilename, &a_stat) < 0) {
+ err = errno;
+ if (vb)
+ pr2serr("unable to stat(%s): %s\n", op->ifilename,
+ safe_strerror(err));
+ return sg_convert_errno(err);
+ }
+ if (op->xfer_len <= 0)
+ op->xfer_len = (int)a_stat.st_size;
+ }
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
+ if (sg_fd < 0) {
+ if (op->verbose)
+ pr2serr(ME "open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ ret = sg_convert_errno(-sg_fd);
+ goto err_out;
+ }
+
+ if (! op->ndob) {
+ prot_en = false;
+ if (0 == op->xfer_len) {
+ res = sg_ll_readcap_16(sg_fd, false /* pmi */, 0 /* llba */,
+ resp_buff, RCAP16_RESP_LEN, true,
+ (vb ? (vb - 1): 0));
+ if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+ pr2serr("Read capacity(16) unit attention, try again\n");
+ res = sg_ll_readcap_16(sg_fd, false, 0, resp_buff,
+ RCAP16_RESP_LEN, true,
+ (vb ? (vb - 1): 0));
+ }
+ if (0 == res) {
+ if (vb > 3)
+ hex2stderr(resp_buff, RCAP16_RESP_LEN, 1);
+ block_size = sg_get_unaligned_be32(resp_buff + 8);
+ prot_en = !!(resp_buff[12] & 0x1);
+ op->xfer_len = block_size;
+ if (prot_en && (op->wrprotect > 0))
+ op->xfer_len += 8;
+ } else if ((SG_LIB_CAT_INVALID_OP == res) ||
+ (SG_LIB_CAT_ILLEGAL_REQ == res)) {
+ if (vb)
+ pr2serr("Read capacity(16) not supported, try Read "
+ "capacity(10)\n");
+ res = sg_ll_readcap_10(sg_fd, false /* pmi */, 0 /* lba */,
+ resp_buff, RCAP10_RESP_LEN, true,
+ (vb ? (vb - 1): 0));
+ if (0 == res) {
+ if (vb > 3)
+ hex2stderr(resp_buff, RCAP10_RESP_LEN, 1);
+ block_size = sg_get_unaligned_be32(resp_buff + 4);
+ op->xfer_len = block_size;
+ } else {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("Read capacity(10): %s\n", b);
+ pr2serr("Unable to calculate block size\n");
+ }
+ } else if (vb) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("Read capacity(16): %s\n", b);
+ pr2serr("Unable to calculate block size\n");
+ }
+ }
+ if (op->xfer_len < 1) {
+ pr2serr("unable to deduce block size, please give '--xferlen=' "
+ "argument\n");
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if (op->xfer_len > MAX_XFER_LEN) {
+ pr2serr("'--xferlen=%d is out of range ( want <= %d)\n",
+ op->xfer_len, MAX_XFER_LEN);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ wBuff = (uint8_t *)sg_memalign(op->xfer_len, 0, &free_wBuff, false);
+ if (NULL == wBuff) {
+ pr2serr("unable to allocate %d bytes of memory with "
+ "sg_memalign()\n", op->xfer_len);
+ ret = sg_convert_errno(ENOMEM);
+ goto err_out;
+ }
+ if (op->ff)
+ memset(wBuff, 0xff, op->xfer_len);
+ if (op->ifilename[0]) {
+ if (got_stdin) {
+ infd = STDIN_FILENO;
+ if (sg_set_binary_mode(STDIN_FILENO) < 0)
+ perror("sg_set_binary_mode");
+ } else {
+ if ((infd = open(op->ifilename, O_RDONLY)) < 0) {
+ ret = sg_convert_errno(errno);
+ snprintf(ebuff, EBUFF_SZ, ME "could not open %.400s for "
+ "reading", op->ifilename);
+ perror(ebuff);
+ goto err_out;
+ } else if (sg_set_binary_mode(infd) < 0)
+ perror("sg_set_binary_mode");
+ }
+ res = read(infd, wBuff, op->xfer_len);
+ if (res < 0) {
+ ret = sg_convert_errno(errno);
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %.400s",
+ op->ifilename);
+ perror(ebuff);
+ if (! got_stdin)
+ close(infd);
+ goto err_out;
+ }
+ if (res < op->xfer_len) {
+ pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+ op->xfer_len, op->ifilename, res);
+ pr2serr(" so pad with 0x0 bytes and continue\n");
+ }
+ if (! got_stdin)
+ close(infd);
+ } else {
+ if (vb)
+ pr2serr("Default data-out buffer set to %d zeros\n",
+ op->xfer_len);
+ if (prot_en && (op->wrprotect > 0)) {
+ /* default for protection is 0xff, rest get 0x0 */
+ memset(wBuff + op->xfer_len - 8, 0xff, 8);
+ if (vb)
+ pr2serr(" ... apart from last 8 bytes which are set to "
+ "0xff\n");
+ }
+ }
+ }
+
+ ret = do_write_same(sg_fd, op, wBuff, &act_cdb_len);
+ if (ret) {
+ sg_get_category_sense_str(ret, sizeof(b), b, vb);
+ pr2serr("Write same(%d): %s\n", act_cdb_len, b);
+ }
+
+err_out:
+ if (free_wBuff)
+ free(free_wBuff);
+ 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 == op->verbose) {
+ if (! sg_if_can2stderr("sg_write_same failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' "
+ "or '-vv' for more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}