xref: /aosp_15_r20/external/sg3_utils/src/sgm_dd.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* A utility program for copying files. Specialised for "files" that
2  * represent devices that understand the SCSI command set.
3  *
4  * Copyright (C) 1999 - 2022 D. Gilbert and P. Allworth
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11 
12    This program is a specialisation of the Unix "dd" command in which
13    either the input or the output file is a scsi generic device. The block
14    size ('bs') is assumed to be 512 if not given. This program complains if
15    'ibs' or 'obs' are given with a value that differs from 'bs' (or the
16    default, 512). If 'if' is not given or 'if=-' then stdin is assumed.
17    If 'of' is not given or 'of=-' then stdout assumed.
18 
19    A non-standard argument "bpt" (blocks per transfer) is added to control
20    the maximum number of blocks in each transfer. The default value is 128.
21    For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
22    in this case) is transferred to or from the sg device in a single SCSI
23    command.
24 
25    This version uses memory-mapped transfers (i.e. mmap() call from the user
26    space) to speed transfers. If both sides of copy are sg devices
27    then only the read side will be mmap-ed, while the write side will
28    use normal IO.
29 
30    This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
31 */
32 
33 #define _XOPEN_SOURCE 600
34 #ifndef _GNU_SOURCE
35 #define _GNU_SOURCE 1
36 #endif
37 
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <stdbool.h>
44 #include <string.h>
45 #include <signal.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #include <limits.h>
49 #define __STDC_FORMAT_MACROS 1
50 #include <inttypes.h>
51 #include <sys/ioctl.h>
52 #include <sys/stat.h>
53 #include <sys/time.h>
54 #include <sys/mman.h>
55 #include <sys/sysmacros.h>
56 #ifndef major
57 #include <sys/types.h>
58 #endif
59 #include <linux/major.h>        /* for MEM_MAJOR, SCSI_GENERIC_MAJOR, etc */
60 #include <linux/fs.h>           /* for BLKSSZGET and friends */
61 
62 #ifdef HAVE_CONFIG_H
63 #include "config.h"
64 #endif
65 #include "sg_lib.h"
66 #include "sg_cmds_basic.h"
67 #include "sg_io_linux.h"
68 #include "sg_unaligned.h"
69 #include "sg_pr2serr.h"
70 
71 
72 static const char * version_str = "1.19 20220118";
73 
74 #define DEF_BLOCK_SIZE 512
75 #define DEF_BLOCKS_PER_TRANSFER 128
76 #define DEF_BLOCKS_PER_2048TRANSFER 32
77 #define DEF_SCSI_CDBSZ 10
78 #define MAX_SCSI_CDBSZ 16
79 #define MAX_BPT_VALUE (1 << 24)         /* used for maximum bs as well */
80 #define MAX_COUNT_SKIP_SEEK (1LL << 48) /* coverity wants upper bound */
81 
82 #define ME "sgm_dd: "
83 
84 
85 #ifndef SG_FLAG_MMAP_IO
86 #define SG_FLAG_MMAP_IO 4
87 #endif
88 
89 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
90 #define READ_CAP_REPLY_LEN 8
91 #define RCAP16_REPLY_LEN 32
92 
93 #define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
94 
95 #ifndef RAW_MAJOR
96 #define RAW_MAJOR 255   /*unlikely value */
97 #endif
98 
99 #define FT_OTHER 1              /* filetype other than one of following */
100 #define FT_SG 2                 /* filetype is sg char device */
101 #define FT_RAW 4                /* filetype is raw char device */
102 #define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
103 #define FT_ST 16                /* filetype is st char device (tape) */
104 #define FT_BLOCK 32             /* filetype is a block device */
105 #define FT_ERROR 64             /* couldn't "stat" file */
106 
107 #define DEV_NULL_MINOR_NUM 3
108 
109 #define MIN_RESERVED_SIZE 8192
110 
111 static int sum_of_resids = 0;
112 
113 static int64_t dd_count = -1;
114 static int64_t req_count = 0;
115 static int64_t in_full = 0;
116 static int in_partial = 0;
117 static int64_t out_full = 0;
118 static int out_partial = 0;
119 static int verbose = 0;
120 static int dry_run = 0;
121 static int progress = 0;        /* accept --progress or -p, does nothing */
122 
123 static bool do_time = false;
124 static bool start_tm_valid = false;
125 static struct timeval start_tm;
126 static int blk_sz = 0;
127 static uint32_t glob_pack_id = 0;       /* pre-increment */
128 
129 static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
130 
131 struct flags_t {
132     bool append;
133     bool dio;
134     bool direct;
135     bool dpo;
136     bool dsync;
137     bool excl;
138     bool fua;
139 };
140 
141 
142 static void
install_handler(int sig_num,void (* sig_handler)(int sig))143 install_handler(int sig_num, void (*sig_handler) (int sig))
144 {
145     struct sigaction sigact;
146     sigaction (sig_num, NULL, &sigact);
147     if (sigact.sa_handler != SIG_IGN)
148     {
149         sigact.sa_handler = sig_handler;
150         sigemptyset (&sigact.sa_mask);
151         sigact.sa_flags = 0;
152         sigaction (sig_num, &sigact, NULL);
153     }
154 }
155 
156 static void
print_stats()157 print_stats()
158 {
159     if (0 != dd_count)
160         pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
161     pr2serr("%" PRId64 "+%d records in\n", in_full - in_partial, in_partial);
162     pr2serr("%" PRId64 "+%d records out\n", out_full - out_partial,
163             out_partial);
164 }
165 
166 static void
calc_duration_throughput(bool contin)167 calc_duration_throughput(bool contin)
168 {
169     double a, b;
170     struct timeval end_tm, res_tm;
171 
172     if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
173         gettimeofday(&end_tm, NULL);
174         res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
175         res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
176         if (res_tm.tv_usec < 0) {
177             --res_tm.tv_sec;
178             res_tm.tv_usec += 1000000;
179         }
180         a = res_tm.tv_sec;
181         a += (0.000001 * res_tm.tv_usec);
182         b = (double)blk_sz * (req_count - dd_count);
183         pr2serr("time to transfer data%s: %d.%06d secs",
184                 (contin ? " so far" : ""), (int)res_tm.tv_sec,
185                 (int)res_tm.tv_usec);
186         if ((a > 0.00001) && (b > 511))
187             pr2serr(" at %.2f MB/sec\n", b / (a * 1000000.0));
188         else
189             pr2serr("\n");
190     }
191 }
192 
193 static void
interrupt_handler(int sig)194 interrupt_handler(int sig)
195 {
196     struct sigaction sigact;
197 
198     sigact.sa_handler = SIG_DFL;
199     sigemptyset (&sigact.sa_mask);
200     sigact.sa_flags = 0;
201     sigaction (sig, &sigact, NULL);
202     pr2serr("Interrupted by signal,");
203     print_stats ();
204     if (do_time)
205         calc_duration_throughput(false);
206     kill (getpid (), sig);
207 }
208 
209 static void
siginfo_handler(int sig)210 siginfo_handler(int sig)
211 {
212     if (sig) { ; }      /* unused, dummy to suppress warning */
213     pr2serr("Progress report, continuing ...\n");
214     print_stats();
215     if (do_time)
216         calc_duration_throughput(true);
217 }
218 
219 static int
dd_filetype(const char * filename)220 dd_filetype(const char * filename)
221 {
222     size_t len = strlen(filename);
223     struct stat st;
224 
225     if ((1 == len) && ('.' == filename[0]))
226         return FT_DEV_NULL;
227     if (stat(filename, &st) < 0)
228         return FT_ERROR;
229     if (S_ISCHR(st.st_mode)) {
230         if ((MEM_MAJOR == major(st.st_rdev)) &&
231             (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
232             return FT_DEV_NULL;
233         if (RAW_MAJOR == major(st.st_rdev))
234             return FT_RAW;
235         if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
236             return FT_SG;
237         if (SCSI_TAPE_MAJOR == major(st.st_rdev))
238             return FT_ST;
239     } else if (S_ISBLK(st.st_mode))
240         return FT_BLOCK;
241     return FT_OTHER;
242 }
243 
244 static char *
dd_filetype_str(int ft,char * buff)245 dd_filetype_str(int ft, char * buff)
246 {
247     int off = 0;
248 
249     if (FT_DEV_NULL & ft)
250         off += sg_scnpr(buff + off, 32, "null device ");
251     if (FT_SG & ft)
252         off += sg_scnpr(buff + off, 32, "SCSI generic (sg) device ");
253     if (FT_BLOCK & ft)
254         off += sg_scnpr(buff + off, 32, "block device ");
255     if (FT_ST & ft)
256         off += sg_scnpr(buff + off, 32, "SCSI tape device ");
257     if (FT_RAW & ft)
258         off += sg_scnpr(buff + off, 32, "raw device ");
259     if (FT_OTHER & ft)
260         off += sg_scnpr(buff + off, 32, "other (perhaps ordinary file) ");
261     if (FT_ERROR & ft)
262         sg_scnpr(buff + off, 32, "unable to 'stat' file ");
263     return buff;
264 }
265 
266 static void
usage()267 usage()
268 {
269     pr2serr("Usage: sgm_dd  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]"
270             " [iflag=FLAGS]\n"
271             "               [obs=BS] [of=OFILE] [oflag=FLAGS] "
272             "[seek=SEEK] [skip=SKIP]\n"
273             "               [--help] [--version]\n\n");
274     pr2serr("               [bpt=BPT] [cdbsz=6|10|12|16] [dio=0|1] "
275             "[fua=0|1|2|3]\n"
276             "               [sync=0|1] [time=0|1] [verbose=VERB] "
277             "[--dry-run] [--verbose]\n\n"
278             "  where:\n"
279             "    bpt         is blocks_per_transfer (default is 128)\n"
280             "    bs          must be device logical block size (default "
281             "512)\n"
282             "    cdbsz       size of SCSI READ or WRITE cdb (default is 10)\n"
283             "    count       number of blocks to copy (def: device size)\n"
284             "    dio         0->indirect IO on write, 1->direct IO on write\n"
285             "                (only when read side is sg device (using mmap))\n"
286             "    fua         force unit access: 0->don't(def), 1->OFILE, "
287             "2->IFILE,\n"
288             "                3->OFILE+IFILE\n"
289             "    if          file or device to read from (def: stdin)\n");
290     pr2serr("    iflag       comma separated list from: [direct,dpo,dsync,"
291             "excl,fua,\n"
292             "                null]\n"
293             "    of          file or device to write to (def: stdout), "
294             "OFILE of '.'\n"
295             "                treated as /dev/null\n"
296             "    oflag       comma separated list from: [append,dio,direct,"
297             "dpo,dsync,\n"
298             "                excl,fua,null]\n"
299             "    seek        block position to start writing to OFILE\n"
300             "    skip        block position to start reading from IFILE\n"
301             "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
302             "after copy\n"
303             "    time        0->no timing(def), 1->time plus calculate "
304             "throughput\n"
305             "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
306             "etc\n"
307             "    --dry-run|-d    prepare but bypass copy/read\n"
308             "    --help|-h       print usage message then exit\n"
309             "    --verbose|-v    increase verbosity\n"
310             "    --version|-V    print version information then exit\n\n"
311             "Copy from IFILE to OFILE, similar to dd command\n"
312             "specialized for SCSI devices for which mmap-ed IO attempted\n");
313 }
314 
315 /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
316 static int
scsi_read_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)317 scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
318 {
319     int res, verb;
320     unsigned int ui;
321     uint8_t rcBuff[RCAP16_REPLY_LEN];
322 
323     verb = (verbose ? verbose - 1: 0);
324     res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, false,
325                            verb);
326     if (0 != res)
327         return res;
328 
329     if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
330         (0xff == rcBuff[3])) {
331 
332         res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, false,
333                                verb);
334         if (0 != res)
335             return res;
336         *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1;
337         *sect_sz = sg_get_unaligned_be32(rcBuff + 8);
338     } else {
339         ui = sg_get_unaligned_be32(rcBuff + 0);
340         /* take care not to sign extend values > 0x7fffffff */
341         *num_sect = (int64_t)ui + 1;
342         *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
343     }
344     if (verb)
345         pr2serr("      number of blocks=%" PRId64 " [0x%" PRIx64 "], block "
346                 "size=%d\n", *num_sect, *num_sect, *sect_sz);
347     return 0;
348 }
349 
350 /* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
351 /* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
352 static int
read_blkdev_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)353 read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
354 {
355 #ifdef BLKSSZGET
356     if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
357         perror("BLKSSZGET ioctl error");
358         return -1;
359     } else {
360  #ifdef BLKGETSIZE64
361         uint64_t ull;
362 
363         if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
364 
365             perror("BLKGETSIZE64 ioctl error");
366             return -1;
367         }
368         *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
369         if (verbose > 1)
370             pr2serr("      [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64
371                     "], logical block size=%d\n", *num_sect, *num_sect,
372                     *sect_sz);
373  #else
374         unsigned long ul;
375 
376         if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
377             perror("BLKGETSIZE ioctl error");
378             return -1;
379         }
380         *num_sect = (int64_t)ul;
381         if (verbose > 1)
382             pr2serr("      [bgs] number of blocks=%" PRId64 " [0x%" PRIx64
383                     "], logical block size=%d\n", *num_sect, *num_sect,
384                     *sect_sz);
385  #endif
386     }
387     return 0;
388 #else
389     if (verbose)
390         pr2serr("      BLKSSZGET+BLKGETSIZE ioctl not available\n");
391     *num_sect = 0;
392     *sect_sz = 0;
393     return -1;
394 #endif
395 }
396 
397 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)398 sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
399                   int64_t start_block, bool write_true, bool fua, bool dpo)
400 {
401     int sz_ind;
402     int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
403     int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
404 
405     memset(cdbp, 0, cdb_sz);
406     if (dpo)
407         cdbp[1] |= 0x10;
408     if (fua)
409         cdbp[1] |= 0x8;
410     switch (cdb_sz) {
411     case 6:
412         sz_ind = 0;
413         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
414                                                rd_opcode[sz_ind]);
415         sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
416         cdbp[4] = (256 == blocks) ? 0 : (uint8_t)blocks;
417         if (blocks > 256) {
418             pr2serr(ME "for 6 byte commands, maximum number of blocks is "
419                     "256\n");
420             return 1;
421         }
422         if ((start_block + blocks - 1) & (~0x1fffff)) {
423             pr2serr(ME "for 6 byte commands, can't address blocks beyond "
424                     "%d\n", 0x1fffff);
425             return 1;
426         }
427         if (dpo || fua) {
428             pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
429                     "supported\n");
430             return 1;
431         }
432         break;
433     case 10:
434         sz_ind = 1;
435         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
436                                                rd_opcode[sz_ind]);
437         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
438         sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
439         if (blocks & (~0xffff)) {
440             pr2serr(ME "for 10 byte commands, maximum number of blocks is "
441                     "%d\n", 0xffff);
442             return 1;
443         }
444         break;
445     case 12:
446         sz_ind = 2;
447         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
448                                                rd_opcode[sz_ind]);
449         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
450         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
451         break;
452     case 16:
453         sz_ind = 3;
454         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
455                                                rd_opcode[sz_ind]);
456         sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
457         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
458         break;
459     default:
460         pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
461                 cdb_sz);
462         return 1;
463     }
464     return 0;
465 }
466 
467 /* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
468  * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
469 static int
sg_read(int sg_fd,uint8_t * buff,int blocks,int64_t from_block,int bs,int cdbsz,bool fua,bool dpo,bool do_mmap)470 sg_read(int sg_fd, uint8_t * buff, int blocks, int64_t from_block,
471         int bs, int cdbsz, bool fua, bool dpo, bool do_mmap)
472 {
473     bool print_cdb_after = false;
474     int res;
475     uint8_t rdCmd[MAX_SCSI_CDBSZ];
476     uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
477     struct sg_io_hdr io_hdr;
478 
479     if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, false, fua,
480                           dpo)) {
481         pr2serr(ME "bad rd cdb build, from_block=%" PRId64 ", blocks=%d\n",
482                 from_block, blocks);
483         return SG_LIB_SYNTAX_ERROR;
484     }
485     memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
486     io_hdr.interface_id = 'S';
487     io_hdr.cmd_len = cdbsz;
488     io_hdr.cmdp = rdCmd;
489     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
490     io_hdr.dxfer_len = bs * blocks;
491     if (! do_mmap)
492         io_hdr.dxferp = buff;
493     io_hdr.mx_sb_len = SENSE_BUFF_LEN;
494     io_hdr.sbp = senseBuff;
495     io_hdr.timeout = DEF_TIMEOUT;
496     io_hdr.pack_id = (int)++glob_pack_id;
497     if (do_mmap)
498         io_hdr.flags |= SG_FLAG_MMAP_IO;
499     if (verbose > 2) {
500         char b[128];
501 
502         pr2serr("    Read cdb: %s\n",
503                 sg_get_command_str(rdCmd, cdbsz, false, sizeof(b), b));
504     }
505 
506 #if 1
507     while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
508            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
509         sleep(1);
510     if (res < 0) {
511         perror(ME "SG_IO error (sg_read)");
512         return -1;
513     }
514 #else
515     while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
516            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
517         ;
518     if (res < 0) {
519         if (ENOMEM == errno)
520             return -2;
521         perror("reading (wr) on sg device, error");
522         return -1;
523     }
524 
525     while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
526            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
527         ;
528     if (res < 0) {
529         perror("reading (rd) on sg device, error");
530         return -1;
531     }
532 #endif
533     if (verbose > 2)
534         pr2serr("      duration=%u ms\n", io_hdr.duration);
535     res =  sg_err_category3(&io_hdr);
536     switch (res) {
537     case SG_LIB_CAT_CLEAN:
538         break;
539     case SG_LIB_CAT_RECOVERED:
540         sg_chk_n_print3("Reading, continuing", &io_hdr, verbose > 1);
541         break;
542     case SG_LIB_CAT_NOT_READY:
543     case SG_LIB_CAT_MEDIUM_HARD:
544         return res;
545     case SG_LIB_CAT_ILLEGAL_REQ:
546         if (verbose)
547             print_cdb_after = true;
548         /* FALL THROUGH */
549     case SG_LIB_CAT_ABORTED_COMMAND:
550     case SG_LIB_CAT_UNIT_ATTENTION:
551     default:
552         sg_chk_n_print3("reading", &io_hdr, verbose > 1);
553         if (print_cdb_after)
554             sg_print_command_len(rdCmd, cdbsz);
555         return res;
556     }
557     sum_of_resids += io_hdr.resid;
558 #ifdef DEBUG
559     pr2serr("duration=%u ms\n", io_hdr.duration);
560 #endif
561     return 0;
562 }
563 
564 /* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
565  * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
566 static int
sg_write(int sg_fd,uint8_t * buff,int blocks,int64_t to_block,int bs,int cdbsz,bool fua,bool dpo,bool do_mmap,bool * diop)567 sg_write(int sg_fd, uint8_t * buff, int blocks, int64_t to_block,
568          int bs, int cdbsz, bool fua, bool dpo, bool do_mmap, bool * diop)
569 {
570     bool print_cdb_after = false;
571     int res;
572     uint8_t wrCmd[MAX_SCSI_CDBSZ];
573     uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
574     struct sg_io_hdr io_hdr SG_C_CPP_ZERO_INIT;
575 
576     if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, true, fua, dpo)) {
577         pr2serr(ME "bad wr cdb build, to_block=%" PRId64 ", blocks=%d\n",
578                 to_block, blocks);
579         return SG_LIB_SYNTAX_ERROR;
580     }
581 
582     io_hdr.interface_id = 'S';
583     io_hdr.cmd_len = cdbsz;
584     io_hdr.cmdp = wrCmd;
585     io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
586     io_hdr.dxfer_len = bs * blocks;
587     if (! do_mmap)
588         io_hdr.dxferp = buff;
589     io_hdr.mx_sb_len = SENSE_BUFF_LEN;
590     io_hdr.sbp = senseBuff;
591     io_hdr.timeout = DEF_TIMEOUT;
592     io_hdr.pack_id = (int)++glob_pack_id;
593     if (do_mmap)
594         io_hdr.flags |= SG_FLAG_MMAP_IO;
595     else if (diop && *diop)
596         io_hdr.flags |= SG_FLAG_DIRECT_IO;
597     if (verbose > 2) {
598         char b[128];
599 
600         pr2serr("    Write cdb: %s\n",
601                 sg_get_command_str(wrCmd, cdbsz, false, sizeof(b), b));
602     }
603 
604 #if 1
605     while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
606            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
607         sleep(1);
608     if (res < 0) {
609         perror(ME "SG_IO error (sg_write)");
610         return -1;
611     }
612 #else
613     while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
614            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
615         ;
616     if (res < 0) {
617         if (ENOMEM == errno)
618             return -2;
619         perror("writing (wr) on sg device, error");
620         return -1;
621     }
622 
623     while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
624            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
625         ;
626     if (res < 0) {
627         perror("writing (rd) on sg device, error");
628         return -1;
629     }
630 #endif
631     if (verbose > 2)
632         pr2serr("      duration=%u ms\n", io_hdr.duration);
633     res = sg_err_category3(&io_hdr);
634     switch (res) {
635     case SG_LIB_CAT_CLEAN:
636         break;
637     case SG_LIB_CAT_RECOVERED:
638         sg_chk_n_print3("Writing, continuing", &io_hdr, verbose > 1);
639         break;
640     case SG_LIB_CAT_NOT_READY:
641     case SG_LIB_CAT_MEDIUM_HARD:
642         return res;
643     case SG_LIB_CAT_ILLEGAL_REQ:
644         if (verbose)
645             print_cdb_after = true;
646         /* FALL THROUGH */
647     case SG_LIB_CAT_ABORTED_COMMAND:
648     case SG_LIB_CAT_UNIT_ATTENTION:
649     default:
650         sg_chk_n_print3("writing", &io_hdr, verbose > 1);
651         if (print_cdb_after)
652             sg_print_command_len(wrCmd, cdbsz);
653         return res;
654     }
655     if (diop && *diop &&
656         ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
657         *diop = false;      /* flag that dio not done (completely) */
658     return 0;
659 }
660 
661 static int
process_flags(const char * arg,struct flags_t * fp)662 process_flags(const char * arg, struct flags_t * fp)
663 {
664     char buff[256];
665     char * cp;
666     char * np;
667 
668     strncpy(buff, arg, sizeof(buff));
669     buff[sizeof(buff) - 1] = '\0';
670     if ('\0' == buff[0]) {
671         pr2serr("no flag found\n");
672         return 1;
673     }
674     cp = buff;
675     do {
676         np = strchr(cp, ',');
677         if (np)
678             *np++ = '\0';
679         if (0 == strcmp(cp, "append"))
680             fp->append = true;
681         else if (0 == strcmp(cp, "dio"))
682             fp->dio = true;
683         else if (0 == strcmp(cp, "direct"))
684             fp->direct = true;
685         else if (0 == strcmp(cp, "dpo"))
686             fp->dpo = true;
687         else if (0 == strcmp(cp, "dsync"))
688             fp->dsync = true;
689         else if (0 == strcmp(cp, "excl"))
690             fp->excl = true;
691         else if (0 == strcmp(cp, "fua"))
692             fp->fua = true;
693         else if (0 == strcmp(cp, "null"))
694             ;
695         else {
696             pr2serr("unrecognised flag: %s\n", cp);
697             return 1;
698         }
699         cp = np;
700     } while (cp);
701     return 0;
702 }
703 
704 /* Returns the number of times 'ch' is found in string 's' given the
705  * string's length. */
706 static int
num_chs_in_str(const char * s,int slen,int ch)707 num_chs_in_str(const char * s, int slen, int ch)
708 {
709     int res = 0;
710 
711     while (--slen >= 0) {
712         if (ch == s[slen])
713             ++res;
714     }
715     return res;
716 }
717 
718 
719 #define STR_SZ 1024
720 #define INOUTF_SZ 512
721 #define EBUFF_SZ 768
722 
723 int
main(int argc,char * argv[])724 main(int argc, char * argv[])
725 {
726     bool bpt_given = false;
727     bool cdbsz_given = false;
728     bool do_coe = false;     /* dummy, just accept + ignore */
729     bool do_sync = false;
730     bool verbose_given = false;
731     bool version_given = false;
732     int res, k, t, infd, outfd, blocks, n, flags, blocks_per, err, keylen;
733     int bpt = DEF_BLOCKS_PER_TRANSFER;
734     int ibs = 0;
735     int in_res_sz = 0;
736     int in_sect_sz;
737     int in_type = FT_OTHER;
738     int obs = 0;
739     int out_res_sz = 0;
740     int out_sect_sz;
741     int out_type = FT_OTHER;
742     int num_dio_not_done = 0;
743     int ret = 0;
744     int scsi_cdbsz_in = DEF_SCSI_CDBSZ;
745     int scsi_cdbsz_out = DEF_SCSI_CDBSZ;
746     size_t psz;
747     int64_t in_num_sect = -1;
748     int64_t out_num_sect = -1;
749     int64_t skip = 0;
750     int64_t seek = 0;
751     char * buf;
752     char * key;
753     uint8_t * wrkPos;
754     uint8_t * wrkBuff = NULL;
755     uint8_t * wrkMmap = NULL;
756     char inf[INOUTF_SZ];
757     char str[STR_SZ];
758     char outf[INOUTF_SZ];
759     char ebuff[EBUFF_SZ];
760     char b[80];
761     struct flags_t in_flags;
762     struct flags_t out_flags;
763 
764 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
765     psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
766 #else
767     psz = 4096;     /* give up, pick likely figure */
768 #endif
769     inf[0] = '\0';
770     outf[0] = '\0';
771     memset(&in_flags, 0, sizeof(in_flags));
772     memset(&out_flags, 0, sizeof(out_flags));
773 
774     for (k = 1; k < argc; k++) {
775         if (argv[k])
776             snprintf(str, STR_SZ, "%s", argv[k]);
777         else
778             continue;
779         for (key = str, buf = key; *buf && *buf != '=';)
780             buf++;
781         if (*buf)
782             *buf++ = '\0';
783         keylen = strlen(key);
784         if (0 == strcmp(key,"bpt")) {
785             bpt = sg_get_num(buf);
786             if (-1 == bpt) {
787                 pr2serr(ME "bad argument to 'bpt'\n");
788                 return SG_LIB_SYNTAX_ERROR;
789             }
790             bpt_given = true;
791         } else if (0 == strcmp(key,"bs")) {
792             blk_sz = sg_get_num(buf);
793             if (-1 == blk_sz) {
794                 pr2serr(ME "bad argument to 'bs'\n");
795                 return SG_LIB_SYNTAX_ERROR;
796             }
797         } else if (0 == strcmp(key,"cdbsz")) {
798             scsi_cdbsz_in = sg_get_num(buf);
799             if ((scsi_cdbsz_in < 6) || (scsi_cdbsz_in > 32)) {
800                 pr2serr(ME "'cdbsz' expects 6, 10, 12, 16 or 32\n");
801                 return SG_LIB_SYNTAX_ERROR;
802             }
803             scsi_cdbsz_out = scsi_cdbsz_in;
804             cdbsz_given = true;
805         } else if (0 == strcmp(key,"coe")) {
806             do_coe = !! sg_get_num(buf);   /* dummy, just accept + ignore */
807             if (do_coe) { ; }   /* unused, dummy to suppress warning */
808         } else if (0 == strcmp(key,"count")) {
809             if (0 != strcmp("-1", buf)) {
810                 dd_count = sg_get_llnum(buf);
811                 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
812                     pr2serr(ME "bad argument to 'count'\n");
813                     return SG_LIB_SYNTAX_ERROR;
814                 }
815             }   /* treat 'count=-1' as calculate count (same as not given) */
816         } else if (0 == strcmp(key,"dio"))
817             out_flags.dio = !! sg_get_num(buf);
818         else if (0 == strcmp(key,"fua")) {
819             n = sg_get_num(buf);
820             if (n & 1)
821                 out_flags.fua = true;
822             if (n & 2)
823                 in_flags.fua = true;
824         } else if (0 == strcmp(key,"ibs")) {
825             ibs = sg_get_num(buf);
826             if ((ibs < 0) || (ibs > MAX_BPT_VALUE)) {
827                 pr2serr(ME "bad argument to 'ibs'\n");
828                 return SG_LIB_SYNTAX_ERROR;
829             }
830         } else if (strcmp(key,"if") == 0) {
831             if ('\0' != inf[0]) {
832                 pr2serr("Second 'if=' argument??\n");
833                 return SG_LIB_CONTRADICT;
834             } else {
835                 memcpy(inf, buf, INOUTF_SZ);
836                 inf[INOUTF_SZ - 1] = '\0';
837             }
838         } else if (0 == strcmp(key, "iflag")) {
839             if (process_flags(buf, &in_flags)) {
840                 pr2serr(ME "bad argument to 'iflag'\n");
841                 return SG_LIB_SYNTAX_ERROR;
842             }
843         } else if (strcmp(key,"of") == 0) {
844             if ('\0' != outf[0]) {
845                 pr2serr("Second 'of=' argument??\n");
846                 return SG_LIB_CONTRADICT;
847             } else {
848                 memcpy(outf, buf, INOUTF_SZ);
849                 outf[INOUTF_SZ - 1] = '\0';
850             }
851         } else if (0 == strcmp(key, "oflag")) {
852             if (process_flags(buf, &out_flags)) {
853                 pr2serr(ME "bad argument to 'oflag'\n");
854                 return SG_LIB_SYNTAX_ERROR;
855             }
856         } else if (0 == strcmp(key,"obs")) {
857             obs = sg_get_num(buf);
858             if ((obs < 0) || (obs > MAX_BPT_VALUE)) {
859                 pr2serr(ME "bad argument to 'obs'\n");
860                 return SG_LIB_SYNTAX_ERROR;
861             }
862         } else if (0 == strcmp(key,"seek")) {
863             seek = sg_get_llnum(buf);
864             if ((seek < 0) || (seek > MAX_COUNT_SKIP_SEEK)) {
865                 pr2serr(ME "bad argument to 'seek'\n");
866                 return SG_LIB_SYNTAX_ERROR;
867             }
868         } else if (0 == strcmp(key,"skip")) {
869             skip = sg_get_llnum(buf);
870             if ((skip < 0) || (skip > MAX_COUNT_SKIP_SEEK)) {
871                 pr2serr(ME "bad argument to 'skip'\n");
872                 return SG_LIB_SYNTAX_ERROR;
873             }
874         } else if (0 == strcmp(key,"sync"))
875             do_sync = !! sg_get_num(buf);
876         else if (0 == strcmp(key,"time"))
877             do_time = sg_get_num(buf);
878         else if (0 == strncmp(key, "verb", 4))
879             verbose = sg_get_num(buf);
880         else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
881             res = 0;
882             n = num_chs_in_str(key + 1, keylen - 1, 'd');
883             dry_run += n;
884             res += n;
885             n = num_chs_in_str(key + 1, keylen - 1, 'h');
886             if (n > 0) {
887                 usage();
888                 return 0;
889             }
890             n = num_chs_in_str(key + 1, keylen - 1, 'p');
891             progress += n;
892             res += n;
893             n = num_chs_in_str(key + 1, keylen - 1, 'v');
894             verbose += n;
895             res += n;
896             n = num_chs_in_str(key + 1, keylen - 1, 'V');
897             if (n > 0)
898                 version_given = true;
899             res += n;
900             if (res < (keylen - 1)) {
901                 pr2serr("Unrecognised short option in '%s', try '--help'\n",
902                         key);
903                 return SG_LIB_SYNTAX_ERROR;
904             }
905         } else if ((0 == strncmp(key, "--dry-run", 9)) ||
906                  (0 == strncmp(key, "--dry_run", 9)))
907             ++dry_run;
908         else if ((0 == strncmp(key, "--help", 6)) ||
909                  (0 == strcmp(key, "-?"))) {
910             usage();
911             return 0;
912         } else if (0 == strncmp(key, "--prog", 6))
913             ++progress;
914         else if (0 == strncmp(key, "--verb", 6))
915             ++verbose;
916         else if (0 == strncmp(key, "--vers", 6))
917             version_given = true;
918         else {
919             pr2serr("Unrecognized option '%s'\n", key);
920             pr2serr("For more information use '--help'\n");
921             return SG_LIB_SYNTAX_ERROR;
922         }
923     }
924 #ifdef DEBUG
925     pr2serr("In DEBUG mode, ");
926     if (verbose_given && version_given) {
927         pr2serr("but override: '-vV' given, zero verbose and continue\n");
928         verbose_given = false;
929         version_given = false;
930         verbose = 0;
931     } else if (! verbose_given) {
932         pr2serr("set '-vv'\n");
933         verbose = 2;
934     } else
935         pr2serr("keep verbose=%d\n", verbose);
936 #else
937     if (verbose_given && version_given)
938         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
939 #endif
940     if (version_given) {
941         pr2serr(ME ": %s\n", version_str);
942         return 0;
943     }
944 
945     if (blk_sz <= 0) {
946         blk_sz = DEF_BLOCK_SIZE;
947         pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
948                 blk_sz);
949     }
950     if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
951         pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
952         usage();
953         return SG_LIB_CONTRADICT;
954     }
955     if ((skip < 0) || (seek < 0)) {
956         pr2serr("skip and seek cannot be negative\n");
957         return SG_LIB_CONTRADICT;
958     }
959     if (out_flags.append && (seek > 0)) {
960         pr2serr("Can't use both append and seek switches\n");
961         return SG_LIB_CONTRADICT;
962     }
963     if ((bpt < 1) || (bpt > MAX_BPT_VALUE)) {
964         pr2serr("bpt must be > 0 and <= %d\n", MAX_BPT_VALUE);
965         return SG_LIB_SYNTAX_ERROR;
966     }
967     /* defaulting transfer size to 128*2048 for CD/DVDs is too large
968        for the block layer in lk 2.6 and results in an EIO on the
969        SG_IO ioctl. So reduce it in that case. */
970     if ((blk_sz >= 2048) && (! bpt_given))
971         bpt = DEF_BLOCKS_PER_2048TRANSFER;
972 
973 #ifdef DEBUG
974     pr2serr(ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64
975             "\n", inf, skip, outf, seek, dd_count);
976 #endif
977     install_handler (SIGINT, interrupt_handler);
978     install_handler (SIGQUIT, interrupt_handler);
979     install_handler (SIGPIPE, interrupt_handler);
980     install_handler (SIGUSR1, siginfo_handler);
981 
982     infd = STDIN_FILENO;
983     outfd = STDOUT_FILENO;
984     if (inf[0] && ('-' != inf[0])) {
985         in_type = dd_filetype(inf);
986         if (verbose > 1)
987             pr2serr(" >> Input file type: %s\n",
988                     dd_filetype_str(in_type, ebuff));
989 
990         if (FT_ERROR == in_type) {
991             pr2serr(ME "unable to access %s\n", inf);
992             return SG_LIB_FILE_ERROR;
993         } else if (FT_ST == in_type) {
994             pr2serr(ME "unable to use scsi tape device %s\n", inf);
995             return SG_LIB_FILE_ERROR;
996         } else if (FT_SG == in_type) {
997             flags = O_RDWR | O_NONBLOCK;
998             if (in_flags.direct)
999                 flags |= O_DIRECT;
1000             if (in_flags.excl)
1001                 flags |= O_EXCL;
1002             if (in_flags.dsync)
1003                 flags |= O_SYNC;
1004             if ((infd = open(inf, flags)) < 0) {
1005                 err = errno;
1006                 snprintf(ebuff, EBUFF_SZ,
1007                          ME "could not open %s for sg reading", inf);
1008                 perror(ebuff);
1009                 return sg_convert_errno(err);
1010             }
1011             res = ioctl(infd, SG_GET_VERSION_NUM, &t);
1012             if ((res < 0) || (t < 30122)) {
1013                 pr2serr(ME "sg driver prior to 3.1.22\n");
1014                 return SG_LIB_FILE_ERROR;
1015             }
1016             in_res_sz = blk_sz * bpt;
1017             if (0 != (in_res_sz % psz)) /* round up to next page */
1018                 in_res_sz = ((in_res_sz / psz) + 1) * psz;
1019             if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
1020                 err = errno;
1021                 perror(ME "SG_GET_RESERVED_SIZE error");
1022                 return sg_convert_errno(err);
1023             }
1024             if (t < MIN_RESERVED_SIZE)
1025                 t = MIN_RESERVED_SIZE;
1026             if (in_res_sz > t) {
1027                 if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
1028                     err = errno;
1029                     perror(ME "SG_SET_RESERVED_SIZE error");
1030                     return sg_convert_errno(err);
1031                 }
1032             }
1033             wrkMmap = (uint8_t *)mmap(NULL, in_res_sz,
1034                                  PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0);
1035             if (MAP_FAILED == wrkMmap) {
1036                 err = errno;
1037                 snprintf(ebuff, EBUFF_SZ,
1038                          ME "error using mmap() on file: %s", inf);
1039                 perror(ebuff);
1040                 return sg_convert_errno(err);
1041             }
1042         } else {
1043             flags = O_RDONLY;
1044             if (in_flags.direct)
1045                 flags |= O_DIRECT;
1046             if (in_flags.excl)
1047                 flags |= O_EXCL;
1048             if (in_flags.dsync)
1049                 flags |= O_SYNC;
1050             if ((infd = open(inf, flags)) < 0) {
1051                 err = errno;
1052                 snprintf(ebuff, EBUFF_SZ,
1053                          ME "could not open %s for reading", inf);
1054                 perror(ebuff);
1055                 return sg_convert_errno(err);
1056             }
1057             else if (skip > 0) {
1058                 off64_t offset = skip;
1059 
1060                 offset *= blk_sz;       /* could exceed 32 bits here! */
1061                 if (lseek64(infd, offset, SEEK_SET) < 0) {
1062                     err = errno;
1063                     snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
1064                              "required position on %s", inf);
1065                     perror(ebuff);
1066                     return sg_convert_errno(err);
1067                 }
1068                 if (verbose > 1)
1069                     pr2serr("  >> skip: lseek64 SEEK_SET, byte offset=0x%"
1070                             PRIx64 "\n", (uint64_t)offset);
1071             }
1072         }
1073     }
1074 
1075     if (outf[0] && ('-' != outf[0])) {
1076         out_type = dd_filetype(outf);
1077         if (verbose > 1)
1078             pr2serr(" >> Output file type: %s\n",
1079                     dd_filetype_str(out_type, ebuff));
1080 
1081         if (FT_ST == out_type) {
1082             pr2serr(ME "unable to use scsi tape device %s\n", outf);
1083             return SG_LIB_FILE_ERROR;
1084         }
1085         else if (FT_SG == out_type) {
1086             flags = O_RDWR | O_NONBLOCK;
1087             if (out_flags.direct)
1088                 flags |= O_DIRECT;
1089             if (out_flags.excl)
1090                 flags |= O_EXCL;
1091             if (out_flags.dsync)
1092                 flags |= O_SYNC;
1093             if ((outfd = open(outf, flags)) < 0) {
1094                 err = errno;
1095                 snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
1096                          "sg writing", outf);
1097                 perror(ebuff);
1098                 return sg_convert_errno(err);
1099             }
1100             res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
1101             if ((res < 0) || (t < 30122)) {
1102                 pr2serr(ME "sg driver prior to 3.1.22\n");
1103                 return SG_LIB_FILE_ERROR;
1104             }
1105             if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
1106                 err = errno;
1107                 perror(ME "SG_GET_RESERVED_SIZE error");
1108                 return sg_convert_errno(err);
1109             }
1110            if (t < MIN_RESERVED_SIZE)
1111                 t = MIN_RESERVED_SIZE;
1112             out_res_sz = blk_sz * bpt;
1113             if (out_res_sz > t) {
1114                 if (ioctl(outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) {
1115                     err = errno;
1116                     perror(ME "SG_SET_RESERVED_SIZE error");
1117                     return sg_convert_errno(err);
1118                 }
1119             }
1120             if (NULL == wrkMmap) {
1121                 wrkMmap = (uint8_t *)mmap(NULL, out_res_sz,
1122                                 PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0);
1123                 if (MAP_FAILED == wrkMmap) {
1124                     err = errno;
1125                     snprintf(ebuff, EBUFF_SZ,
1126                              ME "error using mmap() on file: %s", outf);
1127                     perror(ebuff);
1128                     return sg_convert_errno(err);
1129                 }
1130             }
1131         }
1132         else if (FT_DEV_NULL == out_type)
1133             outfd = -1; /* don't bother opening */
1134         else {
1135             if (FT_RAW != out_type) {
1136                 flags = O_WRONLY | O_CREAT;
1137                 if (out_flags.direct)
1138                     flags |= O_DIRECT;
1139                 if (out_flags.excl)
1140                     flags |= O_EXCL;
1141                 if (out_flags.dsync)
1142                     flags |= O_SYNC;
1143                 if (out_flags.append)
1144                     flags |= O_APPEND;
1145                 if ((outfd = open(outf, flags, 0666)) < 0) {
1146                     err = errno;
1147                     snprintf(ebuff, EBUFF_SZ,
1148                              ME "could not open %s for writing", outf);
1149                     perror(ebuff);
1150                     return sg_convert_errno(err);
1151                 }
1152             }
1153             else {
1154                 if ((outfd = open(outf, O_WRONLY)) < 0) {
1155                     err = errno;
1156                     snprintf(ebuff, EBUFF_SZ, ME "could not open %s "
1157                              "for raw writing", outf);
1158                     perror(ebuff);
1159                     return sg_convert_errno(err);
1160                 }
1161             }
1162             if (seek > 0) {
1163                 off64_t offset = seek;
1164 
1165                 offset *= blk_sz;       /* could exceed 32 bits here! */
1166                 if (lseek64(outfd, offset, SEEK_SET) < 0) {
1167                     err = errno;
1168                     snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to "
1169                              "required position on %s", outf);
1170                     perror(ebuff);
1171                     return sg_convert_errno(err);
1172                 }
1173                 if (verbose > 1)
1174                     pr2serr("   >> seek: lseek64 SEEK_SET, byte offset=0x%"
1175                             PRIx64 "\n", (uint64_t)offset);
1176             }
1177         }
1178     }
1179     if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
1180         pr2serr("Won't default both IFILE to stdin _and_ OFILE to as "
1181                 "stdout\n");
1182         pr2serr("For more information use '--help'\n");
1183         return SG_LIB_CONTRADICT;
1184     }
1185     if (dd_count < 0) {
1186         in_num_sect = -1;
1187         if (FT_SG == in_type) {
1188             res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
1189             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1190                 pr2serr("Unit attention(in), continuing\n");
1191                 res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
1192             } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
1193                 pr2serr("Aborted command(in), continuing\n");
1194                 res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
1195             }
1196             if (0 != res) {
1197                 sg_get_category_sense_str(res, sizeof(b), b, verbose);
1198                 pr2serr("Read capacity (if=%s): %s\n", inf, b);
1199                 in_num_sect = -1;
1200             }
1201         } else if (FT_BLOCK == in_type) {
1202             if (0 != read_blkdev_capacity(infd, &in_num_sect, &in_sect_sz)) {
1203                 pr2serr("Unable to read block capacity on %s\n", inf);
1204                 in_num_sect = -1;
1205             }
1206             if (blk_sz != in_sect_sz) {
1207                 pr2serr("logical block size on %s confusion; bs=%d, from "
1208                         "device=%d\n", inf, blk_sz, in_sect_sz);
1209                 in_num_sect = -1;
1210             }
1211         }
1212         if (in_num_sect > skip)
1213             in_num_sect -= skip;
1214 
1215         out_num_sect = -1;
1216         if (FT_SG == out_type) {
1217             res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
1218             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1219                 pr2serr("Unit attention(out), continuing\n");
1220                 res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
1221             } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
1222                 pr2serr("Aborted command(out), continuing\n");
1223                 res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
1224             }
1225             if (0 != res) {
1226                 sg_get_category_sense_str(res, sizeof(b), b, verbose);
1227                 pr2serr("Read capacity (of=%s): %s\n", inf, b);
1228                 out_num_sect = -1;
1229             }
1230         } else if (FT_BLOCK == out_type) {
1231             if (0 != read_blkdev_capacity(outfd, &out_num_sect,
1232                                           &out_sect_sz)) {
1233                 pr2serr("Unable to read block capacity on %s\n", outf);
1234                 out_num_sect = -1;
1235             }
1236             if (blk_sz != out_sect_sz) {
1237                 pr2serr("logical block size on %s confusion: bs=%d, from "
1238                         "device=%d\n", outf, blk_sz, out_sect_sz);
1239                 out_num_sect = -1;
1240             }
1241         }
1242         if (out_num_sect > seek)
1243             out_num_sect -= seek;
1244 #ifdef DEBUG
1245         pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64 ", "
1246                 "out_num_sect=%" PRId64 "\n", dd_count, in_num_sect,
1247                 out_num_sect);
1248 #endif
1249         if (in_num_sect > 0) {
1250             if (out_num_sect > 0)
1251                 dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
1252                                                           in_num_sect;
1253             else
1254                 dd_count = in_num_sect;
1255         }
1256         else
1257             dd_count = out_num_sect;
1258     }
1259 
1260     if (dd_count < 0) {
1261         pr2serr("Couldn't calculate count, please give one\n");
1262         return SG_LIB_SYNTAX_ERROR;
1263     }
1264     if (! cdbsz_given) {
1265         if ((FT_SG == in_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_in) &&
1266             (((dd_count + skip) > UINT_MAX) || (bpt > USHRT_MAX))) {
1267             pr2serr("Note: SCSI command size increased to 16 bytes (for "
1268                     "'if')\n");
1269             scsi_cdbsz_in = MAX_SCSI_CDBSZ;
1270         }
1271         if ((FT_SG == out_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_out) &&
1272             (((dd_count + seek) > UINT_MAX) || (bpt > USHRT_MAX))) {
1273             pr2serr("Note: SCSI command size increased to 16 bytes (for "
1274                     "'of')\n");
1275             scsi_cdbsz_out = MAX_SCSI_CDBSZ;
1276         }
1277     }
1278 
1279     if (out_flags.dio && (FT_SG != in_type)) {
1280         out_flags.dio = false;
1281         pr2serr(">>> dio only performed on 'of' side when 'if' is an sg "
1282                 "device\n");
1283     }
1284     if (out_flags.dio) {
1285         int fd;
1286         char c;
1287 
1288         if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
1289             if (1 == read(fd, &c, 1)) {
1290                 if ('0' == c)
1291                     pr2serr(">>> %s set to '0' but should be set to '1' for "
1292                             "direct IO\n", sg_allow_dio);
1293             }
1294             close(fd);
1295         }
1296     }
1297 
1298     if (wrkMmap) {
1299         wrkPos = wrkMmap;
1300     } else {
1301         wrkPos = (uint8_t *)sg_memalign(blk_sz * bpt, 0, &wrkBuff,
1302                                         verbose > 3);
1303         if (NULL == wrkPos) {
1304             pr2serr("Not enough user memory\n");
1305             return sg_convert_errno(ENOMEM);
1306         }
1307     }
1308 
1309     blocks_per = bpt;
1310 #ifdef DEBUG
1311     pr2serr("Start of loop, count=%" PRId64 ", blocks_per=%d\n", dd_count,
1312             blocks_per);
1313 #endif
1314     if (dry_run > 0)
1315         goto fini;
1316 
1317     if (do_time) {
1318         start_tm.tv_sec = 0;
1319         start_tm.tv_usec = 0;
1320         gettimeofday(&start_tm, NULL);
1321         start_tm_valid = true;
1322     }
1323     req_count = dd_count;
1324 
1325     if (verbose && (dd_count > 0) && (! out_flags.dio) &&
1326         (FT_SG == in_type) && (FT_SG == out_type))
1327         pr2serr("Since both 'if' and 'of' are sg devices, only do mmap-ed "
1328                 "transfers on 'if'\n");
1329 
1330     while (dd_count > 0) {
1331         blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
1332         if (FT_SG == in_type) {
1333             ret = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in,
1334                           in_flags.fua, in_flags.dpo, true);
1335             if ((SG_LIB_CAT_UNIT_ATTENTION == ret) ||
1336                 (SG_LIB_CAT_ABORTED_COMMAND == ret)) {
1337                 pr2serr("Unit attention or aborted command, continuing "
1338                         "(r)\n");
1339                 ret = sg_read(infd, wrkPos, blocks, skip, blk_sz,
1340                               scsi_cdbsz_in, in_flags.fua, in_flags.dpo,
1341                               true);
1342             }
1343             if (0 != ret) {
1344                 pr2serr("sg_read failed, skip=%" PRId64 "\n", skip);
1345                 break;
1346             }
1347             else
1348                 in_full += blocks;
1349         }
1350         else {
1351             while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
1352                    ((EINTR == errno) || (EAGAIN == errno) ||
1353                     (EBUSY == errno)))
1354                 ;
1355             if (verbose > 2)
1356                 pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz,
1357                         res);
1358             if (ret < 0) {
1359                 snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
1360                          skip);
1361                 perror(ebuff);
1362                 ret = -1;
1363                 break;
1364             }
1365             else if (res < blocks * blk_sz) {
1366                 dd_count = 0;
1367                 blocks = res / blk_sz;
1368                 if ((res % blk_sz) > 0) {
1369                     blocks++;
1370                     in_partial++;
1371                 }
1372             }
1373             in_full += blocks;
1374         }
1375 
1376         if (0 == blocks)
1377             break;      /* read nothing so leave loop */
1378 
1379         if (FT_SG == out_type) {
1380             bool dio_res = out_flags.dio;
1381             bool do_mmap = (FT_SG != in_type);
1382 
1383             ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz, scsi_cdbsz_out,
1384                            out_flags.fua, out_flags.dpo, do_mmap, &dio_res);
1385             if ((SG_LIB_CAT_UNIT_ATTENTION == ret) ||
1386                 (SG_LIB_CAT_ABORTED_COMMAND == ret)) {
1387                 pr2serr("Unit attention or aborted command, continuing (w)\n");
1388                 dio_res = out_flags.dio;
1389                 ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
1390                                scsi_cdbsz_out, out_flags.fua, out_flags.dpo,
1391                                do_mmap, &dio_res);
1392             }
1393             if (0 != ret) {
1394                 pr2serr("sg_write failed, seek=%" PRId64 "\n", seek);
1395                 break;
1396             }
1397             else {
1398                 out_full += blocks;
1399                 if (out_flags.dio && (! dio_res))
1400                     num_dio_not_done++;
1401             }
1402         }
1403         else if (FT_DEV_NULL == out_type)
1404             out_full += blocks; /* act as if written out without error */
1405         else {
1406             while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) &&
1407                    ((EINTR == errno) || (EAGAIN == errno) ||
1408                     (EBUSY == errno)))
1409                 ;
1410             if (verbose > 2)
1411                 pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz,
1412                         res);
1413             if (res < 0) {
1414                 snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%" PRId64 " ",
1415                          seek);
1416                 perror(ebuff);
1417                 break;
1418             }
1419             else if (res < blocks * blk_sz) {
1420                 pr2serr("output file probably full, seek=%" PRId64 " ", seek);
1421                 blocks = res / blk_sz;
1422                 out_full += blocks;
1423                 if ((res % blk_sz) > 0)
1424                     out_partial++;
1425                 break;
1426             }
1427             else
1428                 out_full += blocks;
1429         }
1430         if (dd_count > 0)
1431             dd_count -= blocks;
1432         skip += blocks;
1433         seek += blocks;
1434     }
1435 
1436     if (do_time)
1437         calc_duration_throughput(false);
1438     if (do_sync) {
1439         if (FT_SG == out_type) {
1440             pr2serr(">> Synchronizing cache on %s\n", outf);
1441             res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, false, 0);
1442             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1443                 pr2serr("Unit attention(out), continuing\n");
1444                 res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, false, 0);
1445             }
1446             if (0 != res) {
1447                 sg_get_category_sense_str(res, sizeof(b), b, verbose);
1448                 pr2serr("Synchronize cache(out): %s\n", b);
1449             }
1450         }
1451     }
1452 
1453 fini:
1454     if (wrkBuff)
1455         free(wrkBuff);
1456     if ((STDIN_FILENO != infd) && (infd >= 0))
1457         close(infd);
1458     if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type)) {
1459         if (outfd >= 0)
1460             close(outfd);
1461     }
1462     if ((0 != dd_count) && (0 == dry_run)) {
1463         pr2serr("Some error occurred,");
1464         if (0 == ret)
1465             ret = SG_LIB_CAT_OTHER;
1466     }
1467     print_stats();
1468     if (sum_of_resids)
1469         pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
1470     if (num_dio_not_done)
1471         pr2serr(">> dio requested but _not_ done %d times\n",
1472                 num_dio_not_done);
1473     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1474 }
1475