~ubuntu-branches/ubuntu/wily/sg3-utils/wily

« back to all changes in this revision

Viewing changes to src/sg_compare_and_write.c

  • Committer: Package Import Robot
  • Author(s): Ritesh Raj Sarraf
  • Date: 2013-06-23 16:08:01 UTC
  • mfrom: (1.2.7)
  • Revision ID: package-import@ubuntu.com-20130623160801-7rt7zb2dwk0ba7ut
Tags: 1.36-1
* [69e9dac] Imported Upstream version 1.36
* [cb75936] Add debian compat, level 7
* [68fed25] update README.source
* [3c724fc] Add build-dep autotools-dev
* [e4b9fdd] add destdir to install path
* [7cfff11] Simplify build with debhelper
* [f9a7540] Update symbols for 1.36 release
* [7b0b48d] Enable hardening build

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define _XOPEN_SOURCE 500
 
2
#ifndef _GNU_SOURCE
 
3
#define _GNU_SOURCE
 
4
#endif
 
5
 
 
6
#include <unistd.h>
 
7
#include <fcntl.h>
 
8
#include <stdlib.h>
 
9
#include <string.h>
 
10
#define __STDC_FORMAT_MACROS 1
 
11
#include <inttypes.h>
 
12
#include <getopt.h>
 
13
 
 
14
#ifdef HAVE_CONFIG_H
 
15
#include "config.h"
 
16
#endif
 
17
#include "sg_lib.h"
 
18
#include "sg_cmds_basic.h"
 
19
#include "sg_pt.h"
 
20
 
 
21
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
 
22
*
 
23
*  Copyright (c) 2012-2013, Kaminario Technologies LTD
 
24
*  All rights reserved.
 
25
*  Redistribution and use in source and binary forms, with or without
 
26
*  modification, are permitted provided that the following conditions are met:
 
27
*    * Redistributions of source code must retain the above copyright
 
28
*        notice, this list of conditions and the following disclaimer.
 
29
*    * Redistributions in binary form must reproduce the above copyright
 
30
*        notice, this list of conditions and the following disclaimer in the
 
31
*        documentation and/or other materials provided with the distribution.
 
32
*    * Neither the name of the <organization> nor the
 
33
*        names of its contributors may be used to endorse or promote products
 
34
*        derived from this software without specific prior written permission.
 
35
*
 
36
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
37
*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
38
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
39
*  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 
40
*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
41
*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
42
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
43
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
44
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
45
*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
46
*
 
47
*  This command performs a SCSI COMPARE AND WRITE on the given lba.
 
48
*
 
49
*/
 
50
 
 
51
static const char * version_str = "1.04 20130516";
 
52
 
 
53
#define DEF_BLOCK_SIZE 512
 
54
#define DEF_NUM_BLOCKS (1)
 
55
#define DEF_BLOCKS_PER_TRANSFER 8
 
56
#define DEF_TIMEOUT_SECS 60
 
57
 
 
58
#define COMPARE_AND_WRITE_OPCODE (0x89)
 
59
#define COMPARE_AND_WRITE_CDB_SIZE (16)
 
60
 
 
61
#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
 
62
 
 
63
#define ME "sg_compare_and_write: "
 
64
 
 
65
static struct option long_options[] = {
 
66
        {"dpo", no_argument, 0, 'd'},
 
67
        {"fua", no_argument, 0, 'f'},
 
68
        {"fua_nv", no_argument, 0, 'F'},
 
69
        {"group", required_argument, 0, 'g'},
 
70
        {"help", no_argument, 0, 'h'},
 
71
        {"in", required_argument, 0, 'i'},
 
72
        {"lba", required_argument, 0, 'l'},
 
73
        {"num", required_argument, 0, 'n'},
 
74
        {"timeout", required_argument, 0, 't'},
 
75
        {"verbose", no_argument, 0, 'v'},
 
76
        {"version", no_argument, 0, 'V'},
 
77
        {"wrprotect", required_argument, 0, 'w'},
 
78
        {"xferlen", required_argument, 0, 'x'},
 
79
        {0, 0, 0, 0},
 
80
};
 
81
 
 
82
struct caw_flags {
 
83
        int dpo;
 
84
        int fua;
 
85
        int fua_nv;
 
86
        int group;
 
87
        int wrprotect;
 
88
};
 
89
 
 
90
struct opts_t {
 
91
        char ifilename[256];
 
92
        uint64_t lba;
 
93
        int numblocks;
 
94
        int verbose;
 
95
        int timeout;
 
96
        int xfer_len;
 
97
        const char * device_name;
 
98
        struct caw_flags flags;
 
99
} opts;
 
100
 
 
101
static void
 
102
usage()
 
103
{
 
104
        fprintf(stderr, "Usage: "
 
105
                "sg_compare_and_write [--dpo] [--fua] [--fua_nv] "
 
106
                "[--group=GN] [--help]\n"
 
107
                "                            --in=IF --lba=LBA [--num=NUM] "
 
108
                "[--timeout=TO]\n"
 
109
                "                            [--verbose] [--version] "
 
110
                "[--wrpotect=w]\n"
 
111
                "                            [--xferlen=LEN] DEVICE\n"
 
112
                "  where:\n"
 
113
                "    --dpo|-d            set the dpo bit in cdb (def: "
 
114
                "clear)\n"
 
115
                "    --fua|-f            set the fua bit in cdb (def: "
 
116
                "clear)\n"
 
117
                "    --fua_nv|-F         set the fua_nv bit in cdb (def: "
 
118
                "clear)\n"
 
119
                "    --group=GN|-g GN    GN is GROUP NUMBER to set in "
 
120
                "cdb (def: 0)\n"
 
121
                "    --help|-h           print out usage message\n"
 
122
                "    --in=IF|-i IF       IF is input file, read the compare "
 
123
                "and write buffer\n"
 
124
                "                        from this file\n"
 
125
                "    --lba=LBA|-l LBA    LBA of the first block of the "
 
126
                "compare and write\n"
 
127
                "    --num=NUM|-n NUM    number of blocks to "
 
128
                "compare/write (def: 1)\n"
 
129
                "    --timeout=TO|-t TO    timeout for the command "
 
130
                "(def: 60 secs)\n"
 
131
                "    --verbose|-v        increase verbosity (use '-vv' for "
 
132
                "more)\n"
 
133
                "    --version|-V        print version string then exit\n"
 
134
                "    --wrprotect=WP|-w WP    write protect information "
 
135
                "(def: 0)\n"
 
136
                "    --xferlen=LEN|-x LEN    number of bytes to transfer "
 
137
                "(def: 1024)\n"
 
138
                "                            default is "
 
139
                "(NUM * default_block_size * 2)\n"
 
140
                "\n"
 
141
                "Performs a SCSI COMPARE AND WRITE operation.\n");
 
142
}
 
143
 
 
144
static int
 
145
parse_args(int argc, char* argv[])
 
146
{
 
147
        int c;
 
148
        int lba_given = 0;
 
149
        int if_given = 0;
 
150
        int64_t ll;
 
151
 
 
152
        memset(&opts, 0, sizeof(opts));
 
153
        opts.numblocks = DEF_NUM_BLOCKS;
 
154
        /* COMPARE AND WRITE defines 2*buffers compare + write */
 
155
        opts.xfer_len = 2*DEF_NUM_BLOCKS*DEF_BLOCK_SIZE;
 
156
        opts.timeout = DEF_TIMEOUT_SECS;
 
157
        opts.device_name = NULL;
 
158
        while (1) {
 
159
                int option_index = 0;
 
160
 
 
161
                c = getopt_long(argc, argv, "dfFg:hi:l:n:t:vVw:x:",
 
162
                                long_options, &option_index);
 
163
                if (c == -1)
 
164
                        break;
 
165
 
 
166
                switch (c) {
 
167
                case 'd':
 
168
                        opts.flags.dpo = 1;
 
169
                        break;
 
170
                case 'F':
 
171
                        opts.flags.fua_nv = 1;
 
172
                        break;
 
173
                case 'f':
 
174
                        opts.flags.fua = 1;
 
175
                        break;
 
176
                case 'g':
 
177
                        opts.flags.group = sg_get_num(optarg);
 
178
                        if ((opts.flags.group < 0) ||
 
179
                            (opts.flags.group > 31))  {
 
180
                                fprintf(stderr, "argument to '--group' "
 
181
                                        "expected to be 0 to 31\n");
 
182
                                goto out_err_no_usage;
 
183
                        }
 
184
                        break;
 
185
                case 'h':
 
186
                case '?':
 
187
                        usage();
 
188
                        exit(0);
 
189
                case 'i':
 
190
                        strncpy(opts.ifilename, optarg,
 
191
                                sizeof(opts.ifilename));
 
192
                        if_given = 1;
 
193
                        break;
 
194
                case 'l':
 
195
                        ll = sg_get_llnum(optarg);
 
196
                        if (-1 == ll) {
 
197
                                fprintf(stderr, "bad argument to '--lba'\n");
 
198
                                goto out_err_no_usage;
 
199
                        }
 
200
                        opts.lba = (uint64_t)ll;
 
201
                        lba_given = 1;
 
202
                        break;
 
203
                case 'n':
 
204
                        opts.numblocks = sg_get_num(optarg);
 
205
                        if (opts.numblocks < 0)  {
 
206
                                fprintf(stderr, "bad argument to '--num'\n");
 
207
                                goto out_err_no_usage;
 
208
                        }
 
209
                        break;
 
210
                case 't':
 
211
                        opts.timeout = sg_get_num(optarg);
 
212
                        if (opts.timeout < 0)  {
 
213
                                fprintf(stderr, "bad argument to "
 
214
                                        "'--timeout'\n");
 
215
                                goto out_err_no_usage;
 
216
                        }
 
217
                        break;
 
218
                case 'v':
 
219
                        ++opts.verbose;
 
220
                        break;
 
221
                case 'V':
 
222
                        fprintf(stderr, ME "version: %s\n", version_str);
 
223
                        exit(0);
 
224
                case 'w':
 
225
                        opts.flags.wrprotect = sg_get_num(optarg);
 
226
                        if (opts.flags.wrprotect >> 3) {
 
227
                                fprintf(stderr, "bad argument to "
 
228
                                        "'--wrprotect' not in range 0-7\n");
 
229
                                goto out_err_no_usage;
 
230
                        }
 
231
                        break;
 
232
                case 'x':
 
233
                        opts.xfer_len = sg_get_num(optarg);
 
234
                        if (opts.xfer_len < 0) {
 
235
                                fprintf(stderr, "bad argument to "
 
236
                                        "'--xferlen'\n");
 
237
                                goto out_err_no_usage;
 
238
                        }
 
239
                        break;
 
240
                default:
 
241
                        fprintf(stderr, "unrecognised option code 0x%x ??\n",
 
242
                                c);
 
243
                        goto out_err;
 
244
                }
 
245
        }
 
246
        if (optind < argc) {
 
247
                if (NULL == opts.device_name) {
 
248
                        opts.device_name = argv[optind];
 
249
                        ++optind;
 
250
                }
 
251
                if (optind < argc) {
 
252
                        for (; optind < argc; ++optind)
 
253
                                fprintf(stderr, "Unexpected extra argument: "
 
254
                                        "%s\n", argv[optind]);
 
255
                        goto out_err;
 
256
                }
 
257
        }
 
258
        if (NULL == opts.device_name) {
 
259
                fprintf(stderr, "missing device name!\n");
 
260
                goto out_err;
 
261
        }
 
262
        if (!if_given) {
 
263
                fprintf(stderr, "missing input file\n");
 
264
                goto out_err;
 
265
        }
 
266
        if (!lba_given) {
 
267
                fprintf(stderr, "missing lba\n");
 
268
                goto out_err;
 
269
        }
 
270
        return 0;
 
271
 
 
272
out_err:
 
273
        usage();
 
274
 
 
275
out_err_no_usage:
 
276
        exit(1);
 
277
}
 
278
 
 
279
#define FLAG_FUA        (0x8)
 
280
#define FLAG_FUA_NV     (0x2)
 
281
#define FLAG_DPO        (0x10)
 
282
#define WRPROTECT_MASK  (0x7)
 
283
#define WRPROTECT_SHIFT (5)
 
284
 
 
285
static int
 
286
sg_build_scsi_cdb(unsigned char * cdbp, unsigned int blocks,
 
287
                  int64_t start_block, struct caw_flags flags)
 
288
{
 
289
        memset(cdbp, 0, COMPARE_AND_WRITE_CDB_SIZE);
 
290
        cdbp[0] = COMPARE_AND_WRITE_OPCODE;
 
291
        cdbp[1] = (flags.wrprotect && WRPROTECT_MASK) << WRPROTECT_SHIFT;
 
292
        if (flags.dpo)
 
293
                cdbp[1] |= FLAG_DPO;
 
294
        if (flags.fua)
 
295
                cdbp[1] |= FLAG_FUA;
 
296
        if (flags.fua_nv)
 
297
                cdbp[1] |= FLAG_FUA_NV;
 
298
        cdbp[2] = (unsigned char)((start_block >> 56) & 0xff);
 
299
        cdbp[3] = (unsigned char)((start_block >> 48) & 0xff);
 
300
        cdbp[4] = (unsigned char)((start_block >> 40) & 0xff);
 
301
        cdbp[5] = (unsigned char)((start_block >> 32) & 0xff);
 
302
        cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
 
303
        cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
 
304
        cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
 
305
        cdbp[9] = (unsigned char)(start_block & 0xff);
 
306
        /* cdbp[10-12] are reserved */
 
307
        cdbp[13] = (unsigned char)(blocks & 0xff);
 
308
        cdbp[14] = (unsigned char)(flags.group & 0x1f);
 
309
        return 0;
 
310
}
 
311
 
 
312
static int
 
313
sg_compare_and_write(int sg_fd, unsigned char * buff, int blocks,
 
314
                     int64_t lba, int xfer_len, struct caw_flags flags,
 
315
                     int verbose)
 
316
{
 
317
        int k, sense_cat;
 
318
        unsigned char cawCmd[COMPARE_AND_WRITE_CDB_SIZE];
 
319
        unsigned char senseBuff[SENSE_BUFF_LEN];
 
320
        struct sg_pt_base * ptvp;
 
321
        int res, ret;
 
322
 
 
323
        if (sg_build_scsi_cdb(cawCmd, blocks, lba, flags)) {
 
324
                fprintf(stderr, ME "bad cdb build, lba=0x%"PRIx64", "
 
325
                        "blocks=%d\n", lba, blocks);
 
326
                return -1;
 
327
        }
 
328
        ptvp = construct_scsi_pt_obj();
 
329
        if (NULL == ptvp) {
 
330
                fprintf(sg_warnings_strm, "Could not construct scsit_pt_obj, "
 
331
                        "out of " "memory\n");
 
332
                return -1;
 
333
        }
 
334
 
 
335
        set_scsi_pt_cdb(ptvp, cawCmd, COMPARE_AND_WRITE_CDB_SIZE);
 
336
        set_scsi_pt_sense(ptvp, senseBuff, sizeof(senseBuff));
 
337
        set_scsi_pt_data_out(ptvp, buff, xfer_len);
 
338
        if (verbose > 1) {
 
339
                fprintf(stderr, "    Compare and write cdb: ");
 
340
                for (k = 0; k < COMPARE_AND_WRITE_CDB_SIZE; ++k)
 
341
                        fprintf(stderr, "%02x ", cawCmd[k]);
 
342
                fprintf(stderr, "\n");
 
343
        }
 
344
        if ((verbose > 2) && (xfer_len > 0)) {
 
345
                fprintf(stderr, "    Data-out buffer contents:\n");
 
346
                dStrHex((const char *)buff, xfer_len, 1);
 
347
        }
 
348
        res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT_SECS, verbose);
 
349
        ret = sg_cmds_process_resp(ptvp, "COMPARE AND WRITE", res, 0,
 
350
                                   senseBuff, 1 /* noisy */, verbose,
 
351
                                   &sense_cat);
 
352
        if (-1 == ret)
 
353
                ;
 
354
        else if (-2 == ret) {
 
355
                switch (sense_cat) {
 
356
                case SG_LIB_CAT_NOT_READY:
 
357
                case SG_LIB_CAT_INVALID_OP:
 
358
                case SG_LIB_CAT_UNIT_ATTENTION:
 
359
                case SG_LIB_CAT_ILLEGAL_REQ:
 
360
                case SG_LIB_CAT_ABORTED_COMMAND:
 
361
                        ret = sense_cat;
 
362
                        break;
 
363
                case SG_LIB_CAT_RECOVERED:
 
364
                case SG_LIB_CAT_NO_SENSE:
 
365
                        ret = 0;
 
366
                        break;
 
367
                case SG_LIB_CAT_MEDIUM_HARD:
 
368
                        {
 
369
                                int valid, slen;
 
370
                                uint64_t ull = 0;
 
371
 
 
372
                                slen = get_scsi_pt_sense_len(ptvp);
 
373
                                valid = sg_get_sense_info_fld(senseBuff, slen,
 
374
                                                              &ull);
 
375
                                if (valid)
 
376
                                        fprintf(stderr, "Medium or hardware "
 
377
                                                "error starting at lba=%"
 
378
                                                PRIu64" [0x%"PRIx64"]\n", ull,
 
379
                                                ull);
 
380
                        }
 
381
                        ret = sense_cat;
 
382
                        break;
 
383
                default:
 
384
                        ret = -1;
 
385
                        break;
 
386
                }
 
387
        } else
 
388
                ret = 0;
 
389
 
 
390
        destruct_scsi_pt_obj(ptvp);
 
391
        return ret;
 
392
}
 
393
 
 
394
 
 
395
static int
 
396
open_if(const char * inf)
 
397
{
 
398
        int fd = open(inf, O_RDONLY);
 
399
        if (fd < 0) {
 
400
                fprintf(stderr, ME "open error: %s: %s\n", inf,
 
401
                        safe_strerror(-fd));
 
402
                return -1*SG_LIB_FILE_ERROR;
 
403
        }
 
404
        return fd;
 
405
}
 
406
 
 
407
static int
 
408
open_of(const char * outf, int verbose)
 
409
{
 
410
        int sg_fd = sg_cmds_open_device(outf, 0 /* rw */, verbose);
 
411
        if (sg_fd < 0) {
 
412
                fprintf(stderr, ME "open error: %s: %s\n", outf,
 
413
                        safe_strerror(-sg_fd));
 
414
                return -1*SG_LIB_FILE_ERROR;
 
415
        }
 
416
 
 
417
        return sg_fd;
 
418
}
 
419
 
 
420
#define STR_SZ 1024
 
421
#define INF_SZ 512
 
422
 
 
423
int
 
424
main(int argc, char * argv[])
 
425
{
 
426
        int res;
 
427
        int infd = 0;
 
428
        int outfd = 0;
 
429
        unsigned char * wrkBuff = NULL;
 
430
 
 
431
        res = parse_args(argc, argv);
 
432
        if (res != 0) {
 
433
                fprintf(stderr, "Failed parsing args\n");
 
434
                goto out;
 
435
        }
 
436
 
 
437
        if (opts.verbose)
 
438
                fprintf(stderr, "Running COMPARE AND WRITE command with the "
 
439
                        "following options:\n  in=%s device=%s lba=0x%"
 
440
                        PRIx64 " num_blocks=%d xfer_len=%d timeout=%d\n",
 
441
                        opts.ifilename, opts.device_name, opts.lba,
 
442
                        opts.numblocks, opts.xfer_len, opts.timeout);
 
443
 
 
444
        infd = open_if(opts.ifilename);
 
445
        if (infd <=0) {
 
446
                res = -1*infd;
 
447
                goto out;
 
448
        }
 
449
 
 
450
        outfd = open_of(opts.device_name, opts.verbose);
 
451
        if (outfd <=0) {
 
452
                res = -1*outfd;
 
453
                goto out;
 
454
        }
 
455
 
 
456
        wrkBuff = (unsigned char *)malloc(opts.xfer_len);
 
457
        if (0 == wrkBuff) {
 
458
                fprintf(stderr, "Not enough user memory\n");
 
459
                res = SG_LIB_CAT_OTHER;
 
460
                goto out;
 
461
        }
 
462
 
 
463
        res = read(infd, wrkBuff, opts.xfer_len);
 
464
        if (res < 0) {
 
465
                fprintf(stderr, "Could not read from %s", opts.ifilename);
 
466
                goto out;
 
467
        } else if (res < opts.xfer_len) {
 
468
                fprintf(stderr, "Read only %d bytes (expected %d) from  %s\n",
 
469
                        res, opts.xfer_len, opts.ifilename);
 
470
                goto out;
 
471
        }
 
472
        res = sg_compare_and_write(outfd, wrkBuff, opts.numblocks, opts.lba,
 
473
                opts.xfer_len, opts.flags, opts.verbose);
 
474
 
 
475
out:
 
476
        if (0 != res) {
 
477
                switch (res) {
 
478
                case SG_LIB_CAT_MEDIUM_HARD:
 
479
                        fprintf(stderr, ME "SCSI COMPARE AND WRITE "
 
480
                                "medium/hardware error\n");
 
481
                        break;
 
482
                case SG_LIB_CAT_NOT_READY:
 
483
                        fprintf(stderr, ME "device not compare_and_writey\n");
 
484
                        break;
 
485
                case SG_LIB_CAT_UNIT_ATTENTION:
 
486
                        fprintf(stderr, ME "SCSI COMPARE AND WRITE unit "
 
487
                                "attention\n");
 
488
                        break;
 
489
                case SG_LIB_CAT_ABORTED_COMMAND:
 
490
                        fprintf(stderr, ME "SCSI READ aborted command\n");
 
491
                        break;
 
492
                default:
 
493
                        res = SG_LIB_CAT_OTHER;
 
494
                        fprintf(stderr, ME "SCSI COMPARE AND WRITE failed\n");
 
495
                        break;
 
496
                }
 
497
        }
 
498
 
 
499
        if (wrkBuff)
 
500
                free(wrkBuff);
 
501
        if (infd > 0)
 
502
                close(infd);
 
503
        if (outfd > 0)
 
504
                close(outfd);
 
505
        return res;
 
506
}