xref: /aosp_15_r20/external/sg3_utils/src/sg_read.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  *  A utility program for the Linux OS SCSI generic ("sg") device driver.
3  *    Copyright (C) 2001 - 2022 D. Gilbert
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10 
11    This program reads data from the given SCSI device (typically a disk
12    or cdrom) and discards that data. Its primary goal is to time
13    multiple reads all starting from the same logical address. Its interface
14    is a subset of another member of this package: sg_dd which is a
15    "dd" variant. The input file can be a scsi generic device, a block device,
16    or a seekable file. Streams such as stdin are not acceptable. The block
17    size ('bs') is assumed to be 512 if not given.
18 
19    This version should compile with Linux sg drivers with version numbers
20    >= 30000 . For mmap-ed IO the sg version number >= 30122 .
21 
22 */
23 
24 #define _XOPEN_SOURCE 600
25 #ifndef _GNU_SOURCE
26 #define _GNU_SOURCE 1
27 #endif
28 
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #define __STDC_FORMAT_MACROS 1
40 #include <inttypes.h>
41 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 #include <sys/sysmacros.h>
44 #ifndef major
45 #include <sys/types.h>
46 #endif
47 #include <sys/mman.h>
48 #include <sys/time.h>
49 #include <linux/major.h>
50 
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54 
55 #include "sg_lib.h"
56 #include "sg_io_linux.h"
57 #include "sg_unaligned.h"
58 #include "sg_pr2serr.h"
59 
60 
61 static const char * version_str = "1.38 20220118";
62 
63 #define DEF_BLOCK_SIZE 512
64 #define DEF_BLOCKS_PER_TRANSFER 128
65 #define DEF_SCSI_CDBSZ 10
66 #define MAX_SCSI_CDBSZ 16
67 #define MAX_BPT_VALUE (1 << 24)         /* used for maximum bs as well */
68 #define MAX_COUNT_SKIP_SEEK (1LL << 48) /* coverity wants upper bound */
69 
70 #define ME "sg_read: "
71 
72 #ifndef SG_FLAG_MMAP_IO
73 #define SG_FLAG_MMAP_IO 4
74 #endif
75 
76 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
77 #define DEF_TIMEOUT 40000       /* 40,000 millisecs == 40 seconds */
78 
79 #ifndef RAW_MAJOR
80 #define RAW_MAJOR 255   /*unlikely value */
81 #endif
82 
83 #define FT_OTHER 1              /* filetype other than sg and ... */
84 #define FT_SG 2                 /* filetype is sg char device */
85 #define FT_RAW 4                /* filetype is raw char device */
86 #define FT_BLOCK 8              /* filetype is block device */
87 #define FT_ERROR 64             /* couldn't "stat" file */
88 
89 #define MIN_RESERVED_SIZE 8192
90 
91 static int sum_of_resids = 0;
92 
93 static int64_t dd_count = -1;
94 static int64_t orig_count = 0;
95 static int64_t in_full = 0;
96 static int in_partial = 0;
97 
98 static int pack_id_count = 0;
99 static int verbose = 0;
100 
101 static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
102 
103 
104 static void
install_handler(int sig_num,void (* sig_handler)(int sig))105 install_handler (int sig_num, void (*sig_handler) (int sig))
106 {
107     struct sigaction sigact;
108 
109     sigaction (sig_num, NULL, &sigact);
110     if (sigact.sa_handler != SIG_IGN) {
111         sigact.sa_handler = sig_handler;
112         sigemptyset (&sigact.sa_mask);
113         sigact.sa_flags = 0;
114         sigaction (sig_num, &sigact, NULL);
115     }
116 }
117 
118 static void
print_stats(int iters,const char * str)119 print_stats(int iters, const char * str)
120 {
121     if (orig_count > 0) {
122         if (0 != dd_count)
123             pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
124         pr2serr("%" PRId64 "+%d records in", in_full - in_partial,
125                 in_partial);
126         if (iters > 0)
127             pr2serr(", %s commands issued: %d\n", (str ? str : ""), iters);
128         else
129             pr2serr("\n");
130     } else if (iters > 0)
131         pr2serr("%s commands issued: %d\n", (str ? str : ""), iters);
132 }
133 
134 static void
interrupt_handler(int sig)135 interrupt_handler(int sig)
136 {
137     struct sigaction sigact;
138 
139     sigact.sa_handler = SIG_DFL;
140     sigemptyset (&sigact.sa_mask);
141     sigact.sa_flags = 0;
142     sigaction (sig, &sigact, NULL);
143     pr2serr("Interrupted by signal,");
144     print_stats(0, NULL);
145     kill (getpid (), sig);
146 }
147 
148 static void
siginfo_handler(int sig)149 siginfo_handler(int sig)
150 {
151     if (sig) { ; }      /* unused, dummy to suppress warning */
152     pr2serr("Progress report, continuing ...\n");
153     print_stats(0, NULL);
154 }
155 
156 static int
dd_filetype(const char * filename)157 dd_filetype(const char * filename)
158 {
159     struct stat st;
160 
161     if (stat(filename, &st) < 0)
162         return FT_ERROR;
163     if (S_ISCHR(st.st_mode)) {
164         if (RAW_MAJOR == major(st.st_rdev))
165             return FT_RAW;
166         else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
167             return FT_SG;
168     } else if (S_ISBLK(st.st_mode))
169         return FT_BLOCK;
170     return FT_OTHER;
171 }
172 
173 static void
usage()174 usage()
175 {
176     pr2serr("Usage: sg_read  [blk_sgio=0|1] [bpt=BPT] [bs=BS] "
177             "[cdbsz=6|10|12|16]\n"
178             "                count=COUNT [dio=0|1] [dpo=0|1] [fua=0|1] "
179             "if=IFILE\n"
180             "                [mmap=0|1] [no_dfxer=0|1] [odir=0|1] "
181             "[skip=SKIP]\n"
182             "                [time=TI] [verbose=VERB] [--help] "
183             "[--verbose]\n"
184             "                [--version] "
185             "  where:\n"
186             "    blk_sgio 0->normal IO for block devices, 1->SCSI commands "
187             "via SG_IO\n"
188             "    bpt      is blocks_per_transfer (default is 128, or 64 KiB "
189             "for default BS)\n"
190             "             setting 'bpt=0' will do COUNT zero block SCSI "
191             "READs\n"
192             "    bs       must match sector size if IFILE accessed via SCSI "
193             "commands\n"
194             "             (def=512)\n"
195             "    cdbsz    size of SCSI READ command (default is 10)\n"
196             "    count    total bytes read will be BS*COUNT (if no "
197             "error)\n"
198             "             (if negative, do |COUNT| zero block SCSI READs)\n"
199             "    dio      1-> attempt direct IO on sg device, 0->indirect IO "
200             "(def)\n");
201     pr2serr("    dpo      1-> set disable page out (DPO) in SCSI READs\n"
202             "    fua      1-> set force unit access (FUA) in SCSI READs\n"
203             "    if       an sg, block or raw device, or a seekable file (not "
204             "stdin)\n"
205             "    mmap     1->perform mmap-ed IO on sg device, 0->indirect IO "
206             "(def)\n"
207             "    no_dxfer 1->DMA to kernel buffers only, not user space, "
208             "0->normal(def)\n"
209             "    odir     1->open block device O_DIRECT, 0->don't (def)\n"
210             "    skip     each transfer starts at this logical address "
211             "(def=0)\n"
212             "    time     0->do nothing(def), 1->time from 1st cmd, 2->time "
213             "from 2nd, ...\n"
214             "    verbose  increase level of verbosity (def: 0)\n"
215             "    --help|-h    print this usage message then exit\n"
216             "    --verbose|-v   increase level of verbosity (def: 0)\n"
217             "    --version|-V   print version number then exit\n\n"
218             "Issue SCSI READ commands, each starting from the same logical "
219             "block address\n");
220 }
221 
222 static int
sg_build_scsi_cdb(uint8_t * cdbp,int cdb_sz,unsigned int blocks,int64_t start_block,bool write_true,bool fua,bool dpo)223 sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
224                   int64_t start_block, bool write_true, bool fua, bool dpo)
225 {
226     int sz_ind;
227     int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
228     int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
229 
230     memset(cdbp, 0, cdb_sz);
231     if (dpo)
232         cdbp[1] |= 0x10;
233     if (fua)
234         cdbp[1] |= 0x8;
235     switch (cdb_sz) {
236     case 6:
237         sz_ind = 0;
238         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
239                                                rd_opcode[sz_ind]);
240         sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
241         cdbp[4] = (256 == blocks) ? 0 : (uint8_t)blocks;
242         if (blocks > 256) {
243             pr2serr(ME "for 6 byte commands, maximum number of blocks is "
244                     "256\n");
245             return 1;
246         }
247         if ((start_block + blocks - 1) & (~0x1fffff)) {
248             pr2serr(ME "for 6 byte commands, can't address blocks beyond "
249                     "%d\n", 0x1fffff);
250             return 1;
251         }
252         if (dpo || fua) {
253             pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
254                     "supported\n");
255             return 1;
256         }
257         break;
258     case 10:
259         sz_ind = 1;
260         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
261                                                rd_opcode[sz_ind]);
262         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
263         sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
264         if (blocks & (~0xffff)) {
265             pr2serr(ME "for 10 byte commands, maximum number of blocks is "
266                     "%d\n", 0xffff);
267             return 1;
268         }
269         break;
270     case 12:
271         sz_ind = 2;
272         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
273                                                rd_opcode[sz_ind]);
274         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
275         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
276         break;
277     case 16:
278         sz_ind = 3;
279         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
280                                                rd_opcode[sz_ind]);
281         sg_put_unaligned_be64(start_block, cdbp + 2);
282         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
283         break;
284     default:
285         pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
286                 cdb_sz);
287         return 1;
288     }
289     return 0;
290 }
291 
292 /* -3 medium/hardware error, -2 -> not ready, 0 -> successful,
293    1 -> recoverable (ENOMEM), 2 -> try again (e.g. unit attention),
294    3 -> try again (e.g. aborted command), -1 -> other unrecoverable error */
295 static int
sg_bread(int sg_fd,uint8_t * buff,int blocks,int64_t from_block,int bs,int cdbsz,bool fua,bool dpo,bool * diop,bool do_mmap,bool no_dxfer)296 sg_bread(int sg_fd, uint8_t * buff, int blocks, int64_t from_block, int bs,
297          int cdbsz, bool fua, bool dpo, bool * diop, bool do_mmap,
298          bool no_dxfer)
299 {
300     uint8_t rdCmd[MAX_SCSI_CDBSZ];
301     uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
302     struct sg_io_hdr io_hdr;
303 
304     if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, false, fua,
305                           dpo)) {
306         pr2serr(ME "bad cdb build, from_block=%" PRId64 ", blocks=%d\n",
307                 from_block, blocks);
308         return -1;
309     }
310     memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
311     io_hdr.interface_id = 'S';
312     io_hdr.cmd_len = cdbsz;
313     io_hdr.cmdp = rdCmd;
314     if (blocks > 0) {
315         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
316         io_hdr.dxfer_len = bs * blocks;
317         /* next: shows dxferp unused during mmap-ed IO */
318         if (! do_mmap)
319             io_hdr.dxferp = buff;
320         if (diop && *diop)
321             io_hdr.flags |= SG_FLAG_DIRECT_IO;
322         else if (do_mmap)
323             io_hdr.flags |= SG_FLAG_MMAP_IO;
324         else if (no_dxfer)
325             io_hdr.flags |= SG_FLAG_NO_DXFER;
326     } else
327         io_hdr.dxfer_direction = SG_DXFER_NONE;
328     io_hdr.mx_sb_len = SENSE_BUFF_LEN;
329     io_hdr.sbp = senseBuff;
330     io_hdr.timeout = DEF_TIMEOUT;
331     io_hdr.pack_id = pack_id_count++;
332     if (verbose > 1) {
333         char b[128];
334 
335         pr2serr("    READ cdb: %s\n",
336                 sg_get_command_str(rdCmd, cdbsz, false, sizeof(b), b));
337     }
338 
339     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
340         if (ENOMEM == errno)
341             return 1;
342         perror("reading (SG_IO) on sg device, error");
343         return -1;
344     }
345 
346     if (verbose > 2)
347         pr2serr( "      duration=%u ms\n", io_hdr.duration);
348     switch (sg_err_category3(&io_hdr)) {
349     case SG_LIB_CAT_CLEAN:
350         break;
351     case SG_LIB_CAT_RECOVERED:
352         if (verbose > 1)
353                 sg_chk_n_print3("reading, continue", &io_hdr, true);
354         break;
355     case SG_LIB_CAT_UNIT_ATTENTION:
356         if (verbose)
357             sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
358         return 2;
359     case SG_LIB_CAT_ABORTED_COMMAND:
360         if (verbose)
361             sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
362         return 3;
363     case SG_LIB_CAT_NOT_READY:
364         if (verbose)
365             sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
366         return -2;
367     case SG_LIB_CAT_MEDIUM_HARD:
368         if (verbose)
369             sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
370         return -3;
371     default:
372         sg_chk_n_print3("reading", &io_hdr, !! verbose);
373         return -1;
374     }
375     if (blocks > 0) {
376         if (diop && *diop &&
377             ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
378             *diop = 0;      /* flag that dio not done (completely) */
379         sum_of_resids += io_hdr.resid;
380     }
381     return 0;
382 }
383 
384 /* Returns the number of times 'ch' is found in string 's' given the
385  * string's length. */
386 static int
num_chs_in_str(const char * s,int slen,int ch)387 num_chs_in_str(const char * s, int slen, int ch)
388 {
389     int res = 0;
390 
391     while (--slen >= 0) {
392         if (ch == s[slen])
393             ++res;
394     }
395     return res;
396 }
397 
398 #define STR_SZ 1024
399 #define INF_SZ 512
400 #define EBUFF_SZ 768
401 
402 
403 int
main(int argc,char * argv[])404 main(int argc, char * argv[])
405 {
406     bool count_given = false;
407     bool dio_tmp;
408     bool do_blk_sgio = false;
409     bool do_dio = false;
410     bool do_mmap = false;
411     bool do_odir = false;
412     bool dpo = false;
413     bool fua = false;
414     bool no_dxfer = false;
415     bool verbose_given = false;
416     bool version_given = false;
417     int bs = 0;
418     int bpt = DEF_BLOCKS_PER_TRANSFER;
419     int dio_incomplete = 0;
420     int do_time = 0;
421     int in_type = FT_OTHER;
422     int ret = 0;
423     int scsi_cdbsz = DEF_SCSI_CDBSZ;
424     int res, k, t, buf_sz, iters, infd, blocks, flags, blocks_per, err;
425     int n, keylen;
426     size_t psz;
427     int64_t skip = 0;
428     char * key;
429     char * buf;
430     uint8_t * wrkBuff = NULL;
431     uint8_t * wrkPos = NULL;
432     char inf[INF_SZ];
433     char outf[INF_SZ];
434     char str[STR_SZ];
435     char ebuff[EBUFF_SZ];
436     const char * read_str;
437     struct timeval start_tm, end_tm;
438 
439 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
440     psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
441 #else
442     psz = 4096;     /* give up, pick likely figure */
443 #endif
444     inf[0] = '\0';
445 
446     for (k = 1; k < argc; k++) {
447         if (argv[k]) {
448             strncpy(str, argv[k], STR_SZ);
449             str[STR_SZ - 1] = '\0';
450         } else
451             continue;
452         for (key = str, buf = key; (*buf && (*buf != '=')); )
453             buf++;
454         if (*buf)
455             *buf++ = '\0';
456         keylen = strlen(key);
457         if (0 == strcmp(key,"blk_sgio"))
458             do_blk_sgio = !! sg_get_num(buf);
459         else if (0 == strcmp(key,"bpt")) {
460             bpt = sg_get_num(buf);
461             if ((bpt < 0) || (bpt > MAX_BPT_VALUE)) {
462                 pr2serr( ME "bad argument to 'bpt'\n");
463                 return SG_LIB_SYNTAX_ERROR;
464             }
465         } else if (0 == strcmp(key,"bs")) {
466             bs = sg_get_num(buf);
467             if ((bs < 0) || (bs > MAX_BPT_VALUE)) {
468                 pr2serr( ME "bad argument to 'bs'\n");
469                 return SG_LIB_SYNTAX_ERROR;
470             }
471         } else if (0 == strcmp(key,"cdbsz")) {
472             scsi_cdbsz = sg_get_num(buf);
473             if ((scsi_cdbsz < 0) || (scsi_cdbsz > 32)) {
474                 pr2serr( ME "bad argument to 'cdbsz', expect 6, 10, 12, 16 "
475                         "or 32\n");
476                 return SG_LIB_SYNTAX_ERROR;
477             }
478         } else if (0 == strcmp(key,"count")) {
479             count_given = true;
480             if ('-' == *buf) {
481                 dd_count = sg_get_llnum(buf + 1);
482                 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
483                     pr2serr( ME "bad argument to 'count'\n");
484                     return SG_LIB_SYNTAX_ERROR;
485                 }
486                 dd_count = - dd_count;
487             } else {
488                 dd_count = sg_get_llnum(buf);
489                 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
490                     pr2serr( ME "bad argument to 'count'\n");
491                     return SG_LIB_SYNTAX_ERROR;
492                 }
493             }
494         } else if (0 == strcmp(key,"dio"))
495             do_dio = !! sg_get_num(buf);
496         else if (0 == strcmp(key,"dpo"))
497             dpo = !! sg_get_num(buf);
498         else if (0 == strcmp(key,"fua"))
499             fua = !! sg_get_num(buf);
500         else if (strcmp(key,"if") == 0) {
501             memcpy(inf, buf, INF_SZ - 1);
502             inf[INF_SZ - 1] = '\0';
503         } else if (0 == strcmp(key,"mmap"))
504             do_mmap = !! sg_get_num(buf);
505         else if (0 == strcmp(key,"no_dxfer"))
506             no_dxfer = !! sg_get_num(buf);
507         else if (0 == strcmp(key,"odir"))
508             do_odir = !! sg_get_num(buf);
509         else if (strcmp(key,"of") == 0) {
510             memcpy(outf, buf, INF_SZ - 1);
511             outf[INF_SZ - 1] = '\0';
512         } else if (0 == strcmp(key,"skip")) {
513             skip = sg_get_llnum(buf);
514             if ((skip < 0) || (skip > MAX_COUNT_SKIP_SEEK)) {
515                 pr2serr( ME "bad argument to 'skip'\n");
516                 return SG_LIB_SYNTAX_ERROR;
517             }
518         } else if (0 == strcmp(key,"time"))
519             do_time = sg_get_num(buf);
520         else if (0 == strncmp(key, "verb", 4)) {
521             verbose_given = true;
522             verbose = sg_get_num(buf);
523         } else if (0 == strncmp(key, "--help", 6)) {
524             usage();
525             return 0;
526         } else if (0 == strncmp(key, "--verb", 6)) {
527             verbose_given = true;
528             ++verbose;
529         } else if (0 == strncmp(key, "--vers", 6))
530             version_given = true;
531         else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
532             res = 0;
533             n = num_chs_in_str(key + 1, keylen - 1, 'h');
534             if (n > 0) {
535                 usage();
536                 return 0;
537             }
538             n = num_chs_in_str(key + 1, keylen - 1, 'v');
539             if (n > 0)
540                 verbose_given = true;
541             verbose += n;
542             res += n;
543             n = num_chs_in_str(key + 1, keylen - 1, 'V');
544             if (n > 0)
545                 version_given = true;
546             res += n;
547             if (res < (keylen - 1)) {
548                 pr2serr("Unrecognised short option in '%s', try '--help'\n",
549                         key);
550                 return SG_LIB_SYNTAX_ERROR;
551             }
552         } else {
553             pr2serr( "Unrecognized argument '%s'\n", key);
554             usage();
555             return SG_LIB_SYNTAX_ERROR;
556         }
557     }
558 
559 #ifdef DEBUG
560     pr2serr("In DEBUG mode, ");
561     if (verbose_given && version_given) {
562         pr2serr("but override: '-vV' given, zero verbose and continue\n");
563         verbose_given = false;
564         version_given = false;
565         verbose = 0;
566     } else if (! verbose_given) {
567         pr2serr("set '-vv'\n");
568         verbose = 2;
569     } else
570         pr2serr("keep verbose=%d\n", verbose);
571 #else
572     if (verbose_given && version_given)
573         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
574 #endif
575     if (version_given) {
576         pr2serr( ME ": %s\n", version_str);
577         return 0;
578     }
579 
580     if (bs <= 0) {
581         bs = DEF_BLOCK_SIZE;
582         if ((dd_count > 0) && (bpt > 0))
583             pr2serr( "Assume default 'bs' (block size) of %d bytes\n", bs);
584     }
585     if (! count_given) {
586         pr2serr("'count' must be given\n");
587         usage();
588         return SG_LIB_SYNTAX_ERROR;
589     }
590     if (skip < 0) {
591         pr2serr("skip cannot be negative\n");
592         return SG_LIB_SYNTAX_ERROR;
593     }
594     if (bpt < 1) {
595         if (0 == bpt) {
596             if (dd_count > 0)
597                 dd_count = - dd_count;
598         } else {
599             pr2serr("bpt must be greater than 0\n");
600             return SG_LIB_SYNTAX_ERROR;
601         }
602     }
603     if (do_dio && do_mmap) {
604         pr2serr("cannot select both dio and mmap\n");
605         return SG_LIB_CONTRADICT;
606     }
607     if (no_dxfer && (do_dio || do_mmap)) {
608         pr2serr("cannot select no_dxfer with dio or mmap\n");
609         return SG_LIB_CONTRADICT;
610     }
611 
612     install_handler (SIGINT, interrupt_handler);
613     install_handler (SIGQUIT, interrupt_handler);
614     install_handler (SIGPIPE, interrupt_handler);
615     install_handler (SIGUSR1, siginfo_handler);
616 
617     if (! inf[0]) {
618         pr2serr("must provide 'if=<filename>'\n");
619         usage();
620         return SG_LIB_SYNTAX_ERROR;
621     }
622     if (0 == strcmp("-", inf)) {
623         pr2serr("'-' (stdin) invalid as <filename>\n");
624         usage();
625         return SG_LIB_SYNTAX_ERROR;
626     }
627     in_type = dd_filetype(inf);
628     if (FT_ERROR == in_type) {
629         pr2serr("Unable to access: %s\n", inf);
630         return SG_LIB_FILE_ERROR;
631     } else if ((FT_BLOCK & in_type) && do_blk_sgio)
632         in_type |= FT_SG;
633 
634     if (FT_SG & in_type) {
635         if ((dd_count < 0) && (6 == scsi_cdbsz)) {
636             pr2serr(ME "SCSI READ (6) can't do zero block reads\n");
637             return SG_LIB_SYNTAX_ERROR;
638         }
639         flags = O_RDWR;
640         if (do_odir)
641             flags |= O_DIRECT;
642         if ((infd = open(inf, flags)) < 0) {
643             flags = O_RDONLY;
644             if (do_odir)
645                 flags |= O_DIRECT;
646             if ((infd = open(inf, flags)) < 0) {
647                 err = errno;
648                 snprintf(ebuff, EBUFF_SZ,
649                          ME "could not open %s for sg reading", inf);
650                 perror(ebuff);
651                 return sg_convert_errno(err);
652             }
653         }
654         if (verbose)
655             pr2serr("Opened %s for SG_IO with flags=0x%x\n", inf, flags);
656         if ((dd_count > 0) && (! (FT_BLOCK & in_type))) {
657             if (verbose > 2) {
658                 if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) >= 0)
659                     pr2serr("  SG_GET_RESERVED_SIZE yields: %d\n", t);
660             }
661             t = bs * bpt;
662             if ((do_mmap) && (0 != (t % psz)))
663                 t = ((t / psz) + 1) * psz;    /* round up to next pagesize */
664             res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
665             if (res < 0)
666                 perror(ME "SG_SET_RESERVED_SIZE error");
667             res = ioctl(infd, SG_GET_VERSION_NUM, &t);
668             if ((res < 0) || (t < 30000)) {
669                 pr2serr(ME "sg driver prior to 3.x.y\n");
670                 return SG_LIB_CAT_OTHER;
671             }
672             if (do_mmap && (t < 30122)) {
673                 pr2serr(ME "mmap-ed IO needs a sg driver version >= 3.1.22\n");
674                 return SG_LIB_CAT_OTHER;
675             }
676         }
677     } else {
678         if (do_mmap) {
679             pr2serr(ME "mmap-ed IO only support on sg devices\n");
680             return SG_LIB_CAT_OTHER;
681         }
682         if (dd_count < 0) {
683             pr2serr(ME "negative 'count' only supported with SCSI READs\n");
684             return SG_LIB_CAT_OTHER;
685         }
686         flags = O_RDONLY;
687         if (do_odir)
688             flags |= O_DIRECT;
689         if ((infd = open(inf, flags)) < 0) {
690             err = errno;
691             snprintf(ebuff,  EBUFF_SZ,
692                      ME "could not open %s for reading", inf);
693             perror(ebuff);
694             return sg_convert_errno(err);
695         }
696         if (verbose)
697             pr2serr("Opened %s for Unix reads with flags=0x%x\n", inf, flags);
698         if (skip > 0) {
699             off64_t offset = skip;
700 
701             offset *= bs;       /* could exceed 32 bits here! */
702             if (lseek64(infd, offset, SEEK_SET) < 0) {
703                 err = errno;
704                 snprintf(ebuff,  EBUFF_SZ,
705                     ME "couldn't skip to required position on %s", inf);
706                 perror(ebuff);
707                 return sg_convert_errno(err);
708             }
709         }
710     }
711 
712     if (0 == dd_count)
713         return 0;
714     orig_count = dd_count;
715 
716     if (dd_count > 0) {
717         if (do_dio || do_odir || (FT_RAW & in_type)) {
718             wrkBuff = (uint8_t *)malloc(bs * bpt + psz);
719             if (0 == wrkBuff) {
720                 pr2serr("Not enough user memory for aligned storage\n");
721                 return SG_LIB_CAT_OTHER;
722             }
723             /* perhaps use posix_memalign() instead */
724             wrkPos = (uint8_t *)(((sg_uintptr_t)wrkBuff + psz - 1) &
725                                        (~(psz - 1)));
726         } else if (do_mmap) {
727             wrkPos = (uint8_t *)mmap(NULL, bs * bpt,
728                         PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0);
729             if (MAP_FAILED == wrkPos) {
730                 perror(ME "error from mmap()");
731                 return SG_LIB_CAT_OTHER;
732             }
733         } else {
734             wrkBuff = (uint8_t *)malloc(bs * bpt);
735             if (0 == wrkBuff) {
736                 pr2serr("Not enough user memory\n");
737                 return SG_LIB_CAT_OTHER;
738             }
739             wrkPos = wrkBuff;
740         }
741     }
742 
743     blocks_per = bpt;
744     start_tm.tv_sec = 0;   /* just in case start set condition not met */
745     start_tm.tv_usec = 0;
746 
747     if (verbose && (dd_count < 0))
748         pr2serr("About to issue %" PRId64 " zero block SCSI READs\n",
749                 0 - dd_count);
750 
751     /* main loop */
752     for (iters = 0; dd_count != 0; ++iters) {
753         if ((do_time > 0) && (iters == (do_time - 1)))
754             gettimeofday(&start_tm, NULL);
755         if (dd_count < 0)
756             blocks = 0;
757         else
758             blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
759         if (FT_SG & in_type) {
760             dio_tmp = do_dio;
761             res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
762                            fua, dpo, &dio_tmp, do_mmap, no_dxfer);
763             if (1 == res) {     /* ENOMEM, find what's available+try that */
764                 if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
765                     perror("RESERVED_SIZE ioctls failed");
766                     break;
767                 }
768                 if (buf_sz < MIN_RESERVED_SIZE)
769                     buf_sz = MIN_RESERVED_SIZE;
770                 blocks_per = (buf_sz + bs - 1) / bs;
771                 blocks = blocks_per;
772                 pr2serr("Reducing read to %d blocks per loop\n", blocks_per);
773                 res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
774                                fua, dpo, &dio_tmp, do_mmap, no_dxfer);
775             } else if (2 == res) {
776                 pr2serr("Unit attention, try again (r)\n");
777                 res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
778                                fua, dpo, &dio_tmp, do_mmap, no_dxfer);
779             }
780             if (0 != res) {
781                 switch (res) {
782                 case -3:
783                     ret = SG_LIB_CAT_MEDIUM_HARD;
784                     pr2serr(ME "SCSI READ medium/hardware error\n");
785                     break;
786                 case -2:
787                     ret = SG_LIB_CAT_NOT_READY;
788                     pr2serr(ME "device not ready\n");
789                     break;
790                 case 2:
791                     ret = SG_LIB_CAT_UNIT_ATTENTION;
792                     pr2serr(ME "SCSI READ unit attention\n");
793                     break;
794                 case 3:
795                     ret = SG_LIB_CAT_ABORTED_COMMAND;
796                     pr2serr(ME "SCSI READ aborted command\n");
797                     break;
798                 default:
799                     ret = SG_LIB_CAT_OTHER;
800                     pr2serr(ME "SCSI READ failed\n");
801                     break;
802                 }
803                 break;
804             } else {
805                 in_full += blocks;
806                 if (do_dio && (0 == dio_tmp))
807                     dio_incomplete++;
808             }
809         } else {
810             if (iters > 0) { /* subsequent iteration reset skip position */
811                 off64_t offset = skip;
812 
813                 offset *= bs;       /* could exceed 32 bits here! */
814                 if (lseek64(infd, offset, SEEK_SET) < 0) {
815                     perror(ME "could not reset skip position");
816                     break;
817                 }
818             }
819             while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
820                    (EINTR == errno))
821                 ;
822             if (res < 0) {
823                 snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
824                          skip);
825                 perror(ebuff);
826                 break;
827             } else if (res < blocks * bs) {
828                 pr2serr(ME "short read: wanted/got=%d/%d bytes, stop\n",
829                         blocks * bs, res);
830                 blocks = res / bs;
831                 if ((res % bs) > 0) {
832                     blocks++;
833                     in_partial++;
834                 }
835                 dd_count -= blocks;
836                 in_full += blocks;
837                 break;
838             }
839             in_full += blocks;
840         }
841         if (dd_count > 0)
842             dd_count -= blocks;
843         else if (dd_count < 0)
844             ++dd_count;
845     }
846     read_str = (FT_SG & in_type) ? "SCSI READ" : "read";
847     if (do_time > 0) {
848         gettimeofday(&end_tm, NULL);
849         if (start_tm.tv_sec || start_tm.tv_usec) {
850             struct timeval res_tm;
851             double a, b, c;
852 
853             res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
854             res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
855             if (res_tm.tv_usec < 0) {
856                 --res_tm.tv_sec;
857                 res_tm.tv_usec += 1000000;
858             }
859             a = res_tm.tv_sec;
860             a += (0.000001 * res_tm.tv_usec);
861             if (orig_count > 0) {
862                 b = (double)bs * (orig_count - dd_count);
863                 if (do_time > 1)
864                     c = b - ((double)bs * ((do_time - 1.0) * bpt));
865                 else
866                     c = 0.0;
867             } else {
868                 b = 0.0;
869                 c = 0.0;
870             }
871 
872             if (1 == do_time) {
873                 pr2serr("Time for all %s commands was %d.%06d secs", read_str,
874                         (int)res_tm.tv_sec, (int)res_tm.tv_usec);
875                 if ((orig_count > 0) && (a > 0.00001) && (b > 511))
876                     pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
877                 else
878                     pr2serr("\n");
879             } else if (2 == do_time) {
880                 pr2serr("Time from second %s command to end was %d.%06d secs",
881                         read_str, (int)res_tm.tv_sec,
882                         (int)res_tm.tv_usec);
883                 if ((orig_count > 0) && (a > 0.00001) && (c > 511))
884                     pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
885                 else
886                     pr2serr("\n");
887             } else {
888                 pr2serr("Time from start of %s command "
889                         "#%d to end was %d.%06d secs", read_str, do_time,
890                         (int)res_tm.tv_sec, (int)res_tm.tv_usec);
891                 if ((orig_count > 0) && (a > 0.00001) && (c > 511))
892                     pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
893                 else
894                     pr2serr("\n");
895             }
896             if ((iters > 0) && (a > 0.00001))
897                 pr2serr("Average number of %s commands per second was %.2f\n",
898                         read_str, (double)iters / a);
899         }
900     }
901 
902     if (wrkBuff)
903         free(wrkBuff);
904 
905     close(infd);
906     if (0 != dd_count) {
907         pr2serr("Some error occurred,");
908         if (0 == ret)
909             ret = SG_LIB_CAT_OTHER;
910     }
911     print_stats(iters, read_str);
912 
913     if (dio_incomplete) {
914         int fd;
915         char c;
916 
917         pr2serr(">> Direct IO requested but incomplete %d times\n",
918                 dio_incomplete);
919         if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
920             if (1 == read(fd, &c, 1)) {
921                 if ('0' == c)
922                     pr2serr(">>> %s set to '0' but should be set to '1' for "
923                             "direct IO\n", sg_allow_dio);
924             }
925             close(fd);
926         }
927     }
928     if (sum_of_resids)
929         pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
930     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
931 }
932