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