xref: /aosp_15_r20/external/sg3_utils/src/sg_xcopy.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /* A utility program for copying files. Similar to 'dd' but using
2*44704f69SBart Van Assche  * the 'Extended Copy' command.
3*44704f69SBart Van Assche  *
4*44704f69SBart Van Assche  *  Copyright (c) 2011-2022 Hannes Reinecke, SUSE Labs
5*44704f69SBart Van Assche  *
6*44704f69SBart Van Assche  *  Largely taken from 'sg_dd', which has the
7*44704f69SBart Van Assche  *
8*44704f69SBart Van Assche  *  Copyright (C) 1999 - 2010 D. Gilbert and P. Allworth
9*44704f69SBart Van Assche  *  This program is free software; you can redistribute it and/or modify
10*44704f69SBart Van Assche  *  it under the terms of the GNU General Public License as published by
11*44704f69SBart Van Assche  *  the Free Software Foundation; either version 2, or (at your option)
12*44704f69SBart Van Assche  *  any later version.
13*44704f69SBart Van Assche  *
14*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
15*44704f69SBart Van Assche  *
16*44704f69SBart Van Assche  * This program is a specialisation of the Unix "dd" command in which
17*44704f69SBart Van Assche  * either the input or the output file is a scsi generic device, raw
18*44704f69SBart Van Assche  * device, a block device or a normal file. The block size ('bs') is
19*44704f69SBart Van Assche  * assumed to be 512 if not given. This program complains if 'ibs' or
20*44704f69SBart Van Assche  * 'obs' are given with a value that differs from 'bs' (or the default 512).
21*44704f69SBart Van Assche  * If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
22*44704f69SBart Van Assche  * not given or 'of=-' then stdout assumed.
23*44704f69SBart Van Assche  *
24*44704f69SBart Van Assche  * A non-standard argument "bpt" (blocks per transfer) is added to control
25*44704f69SBart Van Assche  * the maximum number of blocks in each transfer. The default value is 128.
26*44704f69SBart Van Assche  * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
27*44704f69SBart Van Assche  * in this case) is transferred to or from the sg device in a single SCSI
28*44704f69SBart Van Assche  * command.
29*44704f69SBart Van Assche  *
30*44704f69SBart Van Assche  * This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
31*44704f69SBart Van Assche  */
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche #define _XOPEN_SOURCE 600
34*44704f69SBart Van Assche #ifndef _GNU_SOURCE
35*44704f69SBart Van Assche #define _GNU_SOURCE 1
36*44704f69SBart Van Assche #endif
37*44704f69SBart Van Assche 
38*44704f69SBart Van Assche #include <unistd.h>
39*44704f69SBart Van Assche #include <fcntl.h>
40*44704f69SBart Van Assche #include <stdio.h>
41*44704f69SBart Van Assche #include <stdlib.h>
42*44704f69SBart Van Assche #include <stdbool.h>
43*44704f69SBart Van Assche #include <stdarg.h>
44*44704f69SBart Van Assche #include <string.h>
45*44704f69SBart Van Assche #include <signal.h>
46*44704f69SBart Van Assche #include <ctype.h>
47*44704f69SBart Van Assche #include <errno.h>
48*44704f69SBart Van Assche #include <limits.h>
49*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
50*44704f69SBart Van Assche #include <inttypes.h>
51*44704f69SBart Van Assche #include <sys/ioctl.h>
52*44704f69SBart Van Assche #include <sys/stat.h>
53*44704f69SBart Van Assche #include <sys/time.h>
54*44704f69SBart Van Assche #include <sys/file.h>
55*44704f69SBart Van Assche #include <sys/sysmacros.h>
56*44704f69SBart Van Assche #ifndef major
57*44704f69SBart Van Assche #include <sys/types.h>
58*44704f69SBart Van Assche #endif
59*44704f69SBart Van Assche #include <linux/major.h>
60*44704f69SBart Van Assche 
61*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
62*44704f69SBart Van Assche #include "config.h"
63*44704f69SBart Van Assche #endif
64*44704f69SBart Van Assche 
65*44704f69SBart Van Assche #include "sg_lib.h"
66*44704f69SBart Van Assche #include "sg_cmds_basic.h"
67*44704f69SBart Van Assche #include "sg_cmds_extra.h"
68*44704f69SBart Van Assche #include "sg_io_linux.h"
69*44704f69SBart Van Assche #include "sg_unaligned.h"
70*44704f69SBart Van Assche #include "sg_pr2serr.h"
71*44704f69SBart Van Assche 
72*44704f69SBart Van Assche static const char * version_str = "0.73 20220118";
73*44704f69SBart Van Assche 
74*44704f69SBart Van Assche #define ME "sg_xcopy: "
75*44704f69SBart Van Assche 
76*44704f69SBart Van Assche #define STR_SZ 1024
77*44704f69SBart Van Assche #define INOUTF_SZ 512
78*44704f69SBart Van Assche #define EBUFF_SZ 1024
79*44704f69SBart Van Assche 
80*44704f69SBart Van Assche #define DEF_BLOCK_SIZE 512
81*44704f69SBart Van Assche #define DEF_BLOCKS_PER_TRANSFER 128
82*44704f69SBart Van Assche #define MAX_BLOCKS_PER_TRANSFER 65535
83*44704f69SBart Van Assche 
84*44704f69SBart Van Assche #define DEF_MODE_RESP_LEN 252
85*44704f69SBart Van Assche #define RW_ERR_RECOVERY_MP 1
86*44704f69SBart Van Assche #define CACHING_MP 8
87*44704f69SBart Van Assche #define CONTROL_MP 0xa
88*44704f69SBart Van Assche 
89*44704f69SBart Van Assche #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
90*44704f69SBart Van Assche #define READ_CAP_REPLY_LEN 8
91*44704f69SBart Van Assche #define RCAP16_REPLY_LEN 32
92*44704f69SBart Van Assche 
93*44704f69SBart Van Assche #define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
94*44704f69SBart Van Assche 
95*44704f69SBart Van Assche #ifndef UINT32_MAX
96*44704f69SBart Van Assche #define UINT32_MAX ((uint32_t)-1)
97*44704f69SBart Van Assche #endif
98*44704f69SBart Van Assche 
99*44704f69SBart Van Assche #ifndef RAW_MAJOR
100*44704f69SBart Van Assche #define RAW_MAJOR 255   /*unlikely value */
101*44704f69SBart Van Assche #endif
102*44704f69SBart Van Assche 
103*44704f69SBart Van Assche #define SG_LIB_FLOCK_ERR 90
104*44704f69SBart Van Assche 
105*44704f69SBart Van Assche /* In SPC-4 the cdb opcodes have more generic names */
106*44704f69SBart Van Assche #define THIRD_PARTY_COPY_OUT_CMD 0x83
107*44704f69SBart Van Assche #define THIRD_PARTY_COPY_IN_CMD 0x84
108*44704f69SBart Van Assche 
109*44704f69SBart Van Assche /* Third party copy IN (opcode 0x84) and OUT (opcode 0x83) command service
110*44704f69SBart Van Assche  * actions */
111*44704f69SBart Van Assche #define SA_XCOPY_LID1           0x0     /* OUT, originate */
112*44704f69SBart Van Assche #define SA_XCOPY_LID4           0x1     /* OUT, originate */
113*44704f69SBart Van Assche #define SA_POP_TOK              0x10    /* OUT, originate */
114*44704f69SBart Van Assche #define SA_WR_USING_TOK         0x11    /* OUT, originate */
115*44704f69SBart Van Assche #define SA_COPY_ABORT           0x1C    /* OUT, abort */
116*44704f69SBart Van Assche #define SA_COPY_STATUS_LID1     0x0     /* IN, retrieve */
117*44704f69SBart Van Assche #define SA_COPY_DATA_LID1       0x1     /* IN, retrieve */
118*44704f69SBart Van Assche #define SA_COPY_OP_PARAMS       0x3     /* IN, retrieve */
119*44704f69SBart Van Assche #define SA_COPY_FAIL_DETAILS    0x4     /* IN, retrieve */
120*44704f69SBart Van Assche #define SA_COPY_STATUS_LID4     0x5     /* IN, retrieve */
121*44704f69SBart Van Assche #define SA_COPY_DATA_LID4       0x6     /* IN, retrieve */
122*44704f69SBart Van Assche #define SA_ROD_TOK_INFO         0x7     /* IN, retrieve */
123*44704f69SBart Van Assche #define SA_ALL_ROD_TOKS         0x8     /* IN, retrieve */
124*44704f69SBart Van Assche 
125*44704f69SBart Van Assche #define DEF_3PC_OUT_TIMEOUT (10 * 60)   /* is 10 minutes enough? */
126*44704f69SBart Van Assche #define DEF_GROUP_NUM 0x0
127*44704f69SBart Van Assche 
128*44704f69SBart Van Assche #define VPD_DEVICE_ID 0x83
129*44704f69SBart Van Assche #define VPD_3PARTY_COPY 0x8f
130*44704f69SBart Van Assche 
131*44704f69SBart Van Assche #define FT_OTHER 1              /* filetype is probably normal */
132*44704f69SBart Van Assche #define FT_SG 2                 /* filetype is sg or bsg char device */
133*44704f69SBart Van Assche #define FT_RAW 4                /* filetype is raw char device */
134*44704f69SBart Van Assche #define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
135*44704f69SBart Van Assche #define FT_ST 16                /* filetype is st char device (tape) */
136*44704f69SBart Van Assche #define FT_BLOCK 32             /* filetype is block device */
137*44704f69SBart Van Assche #define FT_FIFO 64              /* filetype is a fifo (name pipe) */
138*44704f69SBart Van Assche #define FT_ERROR 128            /* couldn't "stat" file */
139*44704f69SBart Van Assche 
140*44704f69SBart Van Assche #define TD_FC_WWPN 1
141*44704f69SBart Van Assche #define TD_FC_PORT 2
142*44704f69SBart Van Assche #define TD_FC_WWPN_AND_PORT 4
143*44704f69SBart Van Assche #define TD_SPI 8
144*44704f69SBart Van Assche #define TD_VPD 16
145*44704f69SBart Van Assche #define TD_IPV4 32
146*44704f69SBart Van Assche #define TD_ALIAS 64
147*44704f69SBart Van Assche #define TD_RDMA 128
148*44704f69SBart Van Assche #define TD_FW 256
149*44704f69SBart Van Assche #define TD_SAS 512
150*44704f69SBart Van Assche #define TD_IPV6 1024
151*44704f69SBart Van Assche #define TD_IP_COPY_SERVICE 2048
152*44704f69SBart Van Assche #define TD_ROD 4096
153*44704f69SBart Van Assche 
154*44704f69SBart Van Assche #define XCOPY_TO_SRC "XCOPY_TO_SRC"
155*44704f69SBart Van Assche #define XCOPY_TO_DST "XCOPY_TO_DST"
156*44704f69SBart Van Assche #define DEF_XCOPY_SRC0_DST1 1
157*44704f69SBart Van Assche 
158*44704f69SBart Van Assche #define DEV_NULL_MINOR_NUM 3
159*44704f69SBart Van Assche 
160*44704f69SBart Van Assche #define MIN_RESERVED_SIZE 8192
161*44704f69SBart Van Assche 
162*44704f69SBart Van Assche #define MAX_UNIT_ATTENTIONS 10
163*44704f69SBart Van Assche #define MAX_ABORTED_CMDS 256
164*44704f69SBart Van Assche 
165*44704f69SBart Van Assche static int64_t dd_count = -1;
166*44704f69SBart Van Assche static int64_t in_full = 0;
167*44704f69SBart Van Assche static int in_partial = 0;
168*44704f69SBart Van Assche static int64_t out_full = 0;
169*44704f69SBart Van Assche static int out_partial = 0;
170*44704f69SBart Van Assche 
171*44704f69SBart Van Assche static bool do_time = false;
172*44704f69SBart Van Assche static bool start_tm_valid = false;
173*44704f69SBart Van Assche static bool xcopy_flag_cat = false;
174*44704f69SBart Van Assche static bool xcopy_flag_dc = false;
175*44704f69SBart Van Assche static bool xcopy_flag_fco = false;     /* fast copy only, spc5r20 */
176*44704f69SBart Van Assche static int blk_sz = 0;
177*44704f69SBart Van Assche static int list_id_usage = -1;
178*44704f69SBart Van Assche static int priority = 1;
179*44704f69SBart Van Assche static int verbose = 0;
180*44704f69SBart Van Assche static struct timeval start_tm;
181*44704f69SBart Van Assche 
182*44704f69SBart Van Assche 
183*44704f69SBart Van Assche struct xcopy_fp_t {
184*44704f69SBart Van Assche     bool append;
185*44704f69SBart Van Assche     bool excl;
186*44704f69SBart Van Assche     bool flock;
187*44704f69SBart Van Assche     bool pad;     /* Data descriptor PAD bit (residual data treatment) */
188*44704f69SBart Van Assche     bool xcopy_given;
189*44704f69SBart Van Assche     int sect_sz;
190*44704f69SBart Van Assche     int sg_type, sg_fd;
191*44704f69SBart Van Assche     int pdt;     /* Peripheral device type */
192*44704f69SBart Van Assche     dev_t devno;
193*44704f69SBart Van Assche     uint32_t min_bytes;
194*44704f69SBart Van Assche     uint32_t max_bytes;
195*44704f69SBart Van Assche     int64_t num_sect;
196*44704f69SBart Van Assche     char fname[INOUTF_SZ];
197*44704f69SBart Van Assche };
198*44704f69SBart Van Assche 
199*44704f69SBart Van Assche static struct xcopy_fp_t ixcf;
200*44704f69SBart Van Assche static struct xcopy_fp_t oxcf;
201*44704f69SBart Van Assche 
202*44704f69SBart Van Assche static const char * read_cap_str = "Read capacity";
203*44704f69SBart Van Assche static const char * rec_copy_op_params_str = "Receive copy operating "
204*44704f69SBart Van Assche                                              "parameters";
205*44704f69SBart Van Assche 
206*44704f69SBart Van Assche static void calc_duration_throughput(int contin);
207*44704f69SBart Van Assche 
208*44704f69SBart Van Assche 
209*44704f69SBart Van Assche static void
install_handler(int sig_num,void (* sig_handler)(int sig))210*44704f69SBart Van Assche install_handler(int sig_num, void (*sig_handler) (int sig))
211*44704f69SBart Van Assche {
212*44704f69SBart Van Assche     struct sigaction sigact;
213*44704f69SBart Van Assche     sigaction (sig_num, NULL, &sigact);
214*44704f69SBart Van Assche     if (sigact.sa_handler != SIG_IGN)
215*44704f69SBart Van Assche     {
216*44704f69SBart Van Assche         sigact.sa_handler = sig_handler;
217*44704f69SBart Van Assche         sigemptyset (&sigact.sa_mask);
218*44704f69SBart Van Assche         sigact.sa_flags = 0;
219*44704f69SBart Van Assche         sigaction (sig_num, &sigact, NULL);
220*44704f69SBart Van Assche     }
221*44704f69SBart Van Assche }
222*44704f69SBart Van Assche 
223*44704f69SBart Van Assche static void
print_stats(const char * str)224*44704f69SBart Van Assche print_stats(const char * str)
225*44704f69SBart Van Assche {
226*44704f69SBart Van Assche     if (0 != dd_count)
227*44704f69SBart Van Assche         pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
228*44704f69SBart Van Assche     pr2serr("%s%" PRId64 "+%d records in\n", str, in_full - in_partial,
229*44704f69SBart Van Assche             in_partial);
230*44704f69SBart Van Assche     pr2serr("%s%" PRId64 "+%d records out\n", str, out_full - out_partial,
231*44704f69SBart Van Assche             out_partial);
232*44704f69SBart Van Assche }
233*44704f69SBart Van Assche 
234*44704f69SBart Van Assche static void
interrupt_handler(int sig)235*44704f69SBart Van Assche interrupt_handler(int sig)
236*44704f69SBart Van Assche {
237*44704f69SBart Van Assche     struct sigaction sigact;
238*44704f69SBart Van Assche 
239*44704f69SBart Van Assche     sigact.sa_handler = SIG_DFL;
240*44704f69SBart Van Assche     sigemptyset(&sigact.sa_mask);
241*44704f69SBart Van Assche     sigact.sa_flags = 0;
242*44704f69SBart Van Assche     sigaction(sig, &sigact, NULL);
243*44704f69SBart Van Assche     pr2serr("Interrupted by signal,");
244*44704f69SBart Van Assche     if (do_time)
245*44704f69SBart Van Assche         calc_duration_throughput(0);
246*44704f69SBart Van Assche     print_stats("");
247*44704f69SBart Van Assche     kill(getpid (), sig);
248*44704f69SBart Van Assche }
249*44704f69SBart Van Assche 
250*44704f69SBart Van Assche static void
siginfo_handler(int sig)251*44704f69SBart Van Assche siginfo_handler(int sig)
252*44704f69SBart Van Assche {
253*44704f69SBart Van Assche     if (sig) { ; }      /* unused, dummy to suppress warning */
254*44704f69SBart Van Assche     pr2serr("Progress report, continuing ...\n");
255*44704f69SBart Van Assche     if (do_time)
256*44704f69SBart Van Assche         calc_duration_throughput(1);
257*44704f69SBart Van Assche     print_stats("  ");
258*44704f69SBart Van Assche }
259*44704f69SBart Van Assche 
260*44704f69SBart Van Assche static bool bsg_major_checked = false;
261*44704f69SBart Van Assche static int bsg_major = 0;
262*44704f69SBart Van Assche 
263*44704f69SBart Van Assche static void
find_bsg_major(void)264*44704f69SBart Van Assche find_bsg_major(void)
265*44704f69SBart Van Assche {
266*44704f69SBart Van Assche     const char * proc_devices = "/proc/devices";
267*44704f69SBart Van Assche     FILE *fp;
268*44704f69SBart Van Assche     char a[128];
269*44704f69SBart Van Assche     char b[128];
270*44704f69SBart Van Assche     char * cp;
271*44704f69SBart Van Assche     int n;
272*44704f69SBart Van Assche 
273*44704f69SBart Van Assche     if (NULL == (fp = fopen(proc_devices, "r"))) {
274*44704f69SBart Van Assche         if (verbose)
275*44704f69SBart Van Assche             pr2serr("fopen %s failed: %s\n", proc_devices, strerror(errno));
276*44704f69SBart Van Assche         return;
277*44704f69SBart Van Assche     }
278*44704f69SBart Van Assche     while ((cp = fgets(b, sizeof(b), fp))) {
279*44704f69SBart Van Assche         if ((1 == sscanf(b, "%126s", a)) &&
280*44704f69SBart Van Assche             (0 == memcmp(a, "Character", 9)))
281*44704f69SBart Van Assche             break;
282*44704f69SBart Van Assche     }
283*44704f69SBart Van Assche     while (cp && (cp = fgets(b, sizeof(b), fp))) {
284*44704f69SBart Van Assche         if (2 == sscanf(b, "%d %126s", &n, a)) {
285*44704f69SBart Van Assche             if (0 == strcmp("bsg", a)) {
286*44704f69SBart Van Assche                 bsg_major = n;
287*44704f69SBart Van Assche                 break;
288*44704f69SBart Van Assche             }
289*44704f69SBart Van Assche         } else
290*44704f69SBart Van Assche             break;
291*44704f69SBart Van Assche     }
292*44704f69SBart Van Assche     if (verbose > 5) {
293*44704f69SBart Van Assche         if (cp)
294*44704f69SBart Van Assche             pr2serr("found bsg_major=%d\n", bsg_major);
295*44704f69SBart Van Assche         else
296*44704f69SBart Van Assche             pr2serr("found no bsg char device in %s\n", proc_devices);
297*44704f69SBart Van Assche     }
298*44704f69SBart Van Assche     fclose(fp);
299*44704f69SBart Van Assche }
300*44704f69SBart Van Assche 
301*44704f69SBart Van Assche /* Returns a file descriptor on success (0 or greater), -1 for an open
302*44704f69SBart Van Assche  * error, -2 for a standard INQUIRY problem. */
303*44704f69SBart Van Assche static int
open_sg(struct xcopy_fp_t * fp,int vb)304*44704f69SBart Van Assche open_sg(struct xcopy_fp_t * fp, int vb)
305*44704f69SBart Van Assche {
306*44704f69SBart Van Assche     int devmajor, devminor, offset;
307*44704f69SBart Van Assche     struct sg_simple_inquiry_resp sir;
308*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
309*44704f69SBart Van Assche     int len, res;
310*44704f69SBart Van Assche 
311*44704f69SBart Van Assche     devmajor = major(fp->devno);
312*44704f69SBart Van Assche     devminor = minor(fp->devno);
313*44704f69SBart Van Assche 
314*44704f69SBart Van Assche     if (fp->sg_type & FT_SG) {
315*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "%.500s", fp->fname);
316*44704f69SBart Van Assche     } else if (fp->sg_type & FT_BLOCK || fp->sg_type & FT_OTHER) {
317*44704f69SBart Van Assche         int fd;
318*44704f69SBart Van Assche 
319*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "/sys/dev/block/%d:%d/partition",
320*44704f69SBart Van Assche                  devmajor, devminor);
321*44704f69SBart Van Assche         if ((fd = open(ebuff, O_RDONLY)) >= 0) {
322*44704f69SBart Van Assche             ebuff[EBUFF_SZ - 1] = '\0';
323*44704f69SBart Van Assche             len = read(fd, ebuff, EBUFF_SZ - 1);
324*44704f69SBart Van Assche             if (len < 0) {
325*44704f69SBart Van Assche                 perror("read partition");
326*44704f69SBart Van Assche             } else {
327*44704f69SBart Van Assche                 offset = strtoul(ebuff, NULL, 10);
328*44704f69SBart Van Assche                 devminor -= offset;
329*44704f69SBart Van Assche             }
330*44704f69SBart Van Assche             close(fd);
331*44704f69SBart Van Assche         }
332*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "/dev/block/%d:%d", devmajor, devminor);
333*44704f69SBart Van Assche     } else {
334*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "/dev/char/%d:%d", devmajor, devminor);
335*44704f69SBart Van Assche     }
336*44704f69SBart Van Assche     fp->sg_fd = sg_cmds_open_device(ebuff, false /* rw mode */, vb);
337*44704f69SBart Van Assche     if (fp->sg_fd < 0) {
338*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ,
339*44704f69SBart Van Assche                  ME "could not open %s device %d:%d for sg",
340*44704f69SBart Van Assche                  fp->sg_type & FT_BLOCK ? "block" : "char",
341*44704f69SBart Van Assche                  devmajor, devminor);
342*44704f69SBart Van Assche         perror(ebuff);
343*44704f69SBart Van Assche         return -sg_convert_errno(-fp->sg_fd);
344*44704f69SBart Van Assche     }
345*44704f69SBart Van Assche     if (sg_simple_inquiry(fp->sg_fd, &sir, false, vb)) {
346*44704f69SBart Van Assche         pr2serr("INQUIRY failed on %s\n", ebuff);
347*44704f69SBart Van Assche         res = sg_cmds_close_device(fp->sg_fd);
348*44704f69SBart Van Assche         if (res < 0)
349*44704f69SBart Van Assche             pr2serr("sg_cmds_close_device() failed as well\n");
350*44704f69SBart Van Assche         fp->sg_fd = -1;
351*44704f69SBart Van Assche         return -1;
352*44704f69SBart Van Assche     }
353*44704f69SBart Van Assche 
354*44704f69SBart Van Assche     fp->pdt = sir.peripheral_type;
355*44704f69SBart Van Assche     if (vb)
356*44704f69SBart Van Assche         pr2serr("    %s: %.8s  %.16s  %.4s  [pdt=%d, 3pc=%d]\n", fp->fname,
357*44704f69SBart Van Assche                 sir.vendor, sir.product, sir.revision, fp->pdt,
358*44704f69SBart Van Assche                 !! (0x8 & sir.byte_5));
359*44704f69SBart Van Assche 
360*44704f69SBart Van Assche     return fp->sg_fd;
361*44704f69SBart Van Assche }
362*44704f69SBart Van Assche 
363*44704f69SBart Van Assche static int
dd_filetype(struct xcopy_fp_t * fp)364*44704f69SBart Van Assche dd_filetype(struct xcopy_fp_t * fp)
365*44704f69SBart Van Assche {
366*44704f69SBart Van Assche     struct stat st;
367*44704f69SBart Van Assche     size_t len = strlen(fp->fname);
368*44704f69SBart Van Assche 
369*44704f69SBart Van Assche     if ((1 == len) && ('.' == fp->fname[0]))
370*44704f69SBart Van Assche         return FT_DEV_NULL;
371*44704f69SBart Van Assche     if (stat(fp->fname, &st) < 0)
372*44704f69SBart Van Assche         return FT_ERROR;
373*44704f69SBart Van Assche     if (S_ISCHR(st.st_mode)) {
374*44704f69SBart Van Assche         fp->devno = st.st_rdev;
375*44704f69SBart Van Assche         /* major() and minor() defined in sys/sysmacros.h */
376*44704f69SBart Van Assche         if ((MEM_MAJOR == major(st.st_rdev)) &&
377*44704f69SBart Van Assche             (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
378*44704f69SBart Van Assche             return FT_DEV_NULL;
379*44704f69SBart Van Assche         if (RAW_MAJOR == major(st.st_rdev))
380*44704f69SBart Van Assche             return FT_RAW;
381*44704f69SBart Van Assche         if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
382*44704f69SBart Van Assche             return FT_SG;
383*44704f69SBart Van Assche         if (SCSI_TAPE_MAJOR == major(st.st_rdev))
384*44704f69SBart Van Assche             return FT_ST;
385*44704f69SBart Van Assche         if (! bsg_major_checked) {
386*44704f69SBart Van Assche             bsg_major_checked = true;
387*44704f69SBart Van Assche             find_bsg_major();
388*44704f69SBart Van Assche         }
389*44704f69SBart Van Assche         if (bsg_major == (int)major(st.st_rdev))
390*44704f69SBart Van Assche             return FT_SG;
391*44704f69SBart Van Assche     } else if (S_ISBLK(st.st_mode)) {
392*44704f69SBart Van Assche         fp->devno = st.st_rdev;
393*44704f69SBart Van Assche         return FT_BLOCK;
394*44704f69SBart Van Assche     } else if (S_ISFIFO(st.st_mode)) {
395*44704f69SBart Van Assche         fp->devno = st.st_dev;
396*44704f69SBart Van Assche         return FT_FIFO;
397*44704f69SBart Van Assche     }
398*44704f69SBart Van Assche     fp->devno = st.st_dev;
399*44704f69SBart Van Assche     return FT_OTHER | FT_BLOCK;
400*44704f69SBart Van Assche }
401*44704f69SBart Van Assche 
402*44704f69SBart Van Assche 
403*44704f69SBart Van Assche static char *
dd_filetype_str(int ft,char * buff)404*44704f69SBart Van Assche dd_filetype_str(int ft, char * buff)
405*44704f69SBart Van Assche {
406*44704f69SBart Van Assche     int off = 0;
407*44704f69SBart Van Assche 
408*44704f69SBart Van Assche     if (FT_DEV_NULL & ft)
409*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "null device ");
410*44704f69SBart Van Assche     if (FT_SG & ft)
411*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "SCSI generic (sg) device ");
412*44704f69SBart Van Assche     if (FT_BLOCK & ft)
413*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "block device ");
414*44704f69SBart Van Assche     if (FT_FIFO & ft)
415*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "fifo (named pipe) ");
416*44704f69SBart Van Assche     if (FT_ST & ft)
417*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "SCSI tape device ");
418*44704f69SBart Van Assche     if (FT_RAW & ft)
419*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "raw device ");
420*44704f69SBart Van Assche     if (FT_OTHER & ft)
421*44704f69SBart Van Assche         off += sg_scnpr(buff + off, 32, "other (perhaps ordinary file) ");
422*44704f69SBart Van Assche     if (FT_ERROR & ft)
423*44704f69SBart Van Assche         sg_scnpr(buff + off, 32, "unable to 'stat' file ");
424*44704f69SBart Van Assche     return buff;
425*44704f69SBart Van Assche }
426*44704f69SBart Van Assche 
427*44704f69SBart Van Assche static int
simplified_ft(const struct xcopy_fp_t * xfp)428*44704f69SBart Van Assche simplified_ft(const struct xcopy_fp_t * xfp)
429*44704f69SBart Van Assche {
430*44704f69SBart Van Assche     int ftype = xfp->sg_type;
431*44704f69SBart Van Assche 
432*44704f69SBart Van Assche     switch (ftype) {
433*44704f69SBart Van Assche     case FT_BLOCK:
434*44704f69SBart Van Assche     case FT_ST:
435*44704f69SBart Van Assche     case FT_OTHER:      /* typically regular file */
436*44704f69SBart Van Assche     case FT_DEV_NULL:
437*44704f69SBart Van Assche     case FT_FIFO:
438*44704f69SBart Van Assche     case FT_ERROR:
439*44704f69SBart Van Assche         return ftype;
440*44704f69SBart Van Assche     default:
441*44704f69SBart Van Assche         if (FT_SG & ftype) {
442*44704f69SBart Van Assche             if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* D-A or RBC */
443*44704f69SBart Van Assche             return FT_BLOCK;
444*44704f69SBart Van Assche         else if (0x1 == xfp->pdt)
445*44704f69SBart Van Assche             return FT_ST;
446*44704f69SBart Van Assche         }
447*44704f69SBart Van Assche         return FT_OTHER;
448*44704f69SBart Van Assche     }
449*44704f69SBart Van Assche }
450*44704f69SBart Van Assche 
451*44704f69SBart Van Assche static int
seg_desc_from_dd_type(int in_ft,int in_off,int out_ft,int out_off)452*44704f69SBart Van Assche seg_desc_from_dd_type(int in_ft, int in_off, int out_ft, int out_off)
453*44704f69SBart Van Assche {
454*44704f69SBart Van Assche     int desc_type = -1;
455*44704f69SBart Van Assche 
456*44704f69SBart Van Assche     switch (in_ft) {
457*44704f69SBart Van Assche     case FT_BLOCK:
458*44704f69SBart Van Assche         switch (out_ft) {
459*44704f69SBart Van Assche         case FT_ST:
460*44704f69SBart Van Assche             if (out_off)
461*44704f69SBart Van Assche                 break;
462*44704f69SBart Van Assche 
463*44704f69SBart Van Assche             if (in_off)
464*44704f69SBart Van Assche                 desc_type = 0x8;
465*44704f69SBart Van Assche             else
466*44704f69SBart Van Assche                 desc_type = 0;
467*44704f69SBart Van Assche             break;
468*44704f69SBart Van Assche         case FT_BLOCK:
469*44704f69SBart Van Assche             if (in_off || out_off)
470*44704f69SBart Van Assche                 desc_type = 0xA;
471*44704f69SBart Van Assche             else
472*44704f69SBart Van Assche                 desc_type = 2;
473*44704f69SBart Van Assche             break;
474*44704f69SBart Van Assche         default:
475*44704f69SBart Van Assche             break;
476*44704f69SBart Van Assche         }
477*44704f69SBart Van Assche         break;
478*44704f69SBart Van Assche     case FT_ST:
479*44704f69SBart Van Assche         if (in_off)
480*44704f69SBart Van Assche             break;
481*44704f69SBart Van Assche 
482*44704f69SBart Van Assche         switch (out_ft) {
483*44704f69SBart Van Assche         case FT_ST:
484*44704f69SBart Van Assche             if (!out_off) {
485*44704f69SBart Van Assche                 desc_type = 3;
486*44704f69SBart Van Assche                 break;
487*44704f69SBart Van Assche             }
488*44704f69SBart Van Assche             break;
489*44704f69SBart Van Assche         case FT_BLOCK:
490*44704f69SBart Van Assche             if (out_off)
491*44704f69SBart Van Assche                 desc_type = 9;
492*44704f69SBart Van Assche             else
493*44704f69SBart Van Assche                 desc_type = 3;
494*44704f69SBart Van Assche             break;
495*44704f69SBart Van Assche         case FT_DEV_NULL:
496*44704f69SBart Van Assche             desc_type = 6;
497*44704f69SBart Van Assche             break;
498*44704f69SBart Van Assche         default:
499*44704f69SBart Van Assche             break;
500*44704f69SBart Van Assche         }
501*44704f69SBart Van Assche         break;
502*44704f69SBart Van Assche     default:
503*44704f69SBart Van Assche         break;
504*44704f69SBart Van Assche     }
505*44704f69SBart Van Assche 
506*44704f69SBart Van Assche     return desc_type;
507*44704f69SBart Van Assche }
508*44704f69SBart Van Assche 
509*44704f69SBart Van Assche static void
usage(int n_help)510*44704f69SBart Van Assche usage(int n_help)
511*44704f69SBart Van Assche {
512*44704f69SBart Van Assche     if (n_help < 2)
513*44704f69SBart Van Assche         goto primary_help;
514*44704f69SBart Van Assche     else
515*44704f69SBart Van Assche         goto secondary_help;
516*44704f69SBart Van Assche 
517*44704f69SBart Van Assche primary_help:
518*44704f69SBart Van Assche     pr2serr("Usage: "
519*44704f69SBart Van Assche             "sg_xcopy [app=0|1] [bpt=BPT] [bs=BS] [cat=0|1] [conv=CONV]\n"
520*44704f69SBart Van Assche             "                [count=COUNT] [dc=0|1] [ibs=BS]\n"
521*44704f69SBart Van Assche             "                [id_usage=hold|discard|disable] [if=IFILE] "
522*44704f69SBart Van Assche             "[iflag=FLAGS]\n"
523*44704f69SBart Van Assche             "                [list_id=ID] [obs=BS] [of=OFILE] "
524*44704f69SBart Van Assche             "[oflag=FLAGS] [prio=PRIO]\n"
525*44704f69SBart Van Assche             "                [seek=SEEK] [skip=SKIP] [time=0|1] "
526*44704f69SBart Van Assche             "[verbose=VERB]\n"
527*44704f69SBart Van Assche             "                [--help] [--on_dst|--on_src] [--verbose] "
528*44704f69SBart Van Assche             "[--version]\n\n"
529*44704f69SBart Van Assche             "  where:\n"
530*44704f69SBart Van Assche             "    app         if argument is 1 then open OFILE in append "
531*44704f69SBart Van Assche             "mode\n"
532*44704f69SBart Van Assche             "    bpt         is blocks_per_transfer (default: 128)\n"
533*44704f69SBart Van Assche             "    bs          block size (default is 512)\n");
534*44704f69SBart Van Assche     pr2serr("    cat         xcopy segment descriptor CAT bit (default: "
535*44704f69SBart Van Assche             "0)\n"
536*44704f69SBart Van Assche             "    conv        ignored\n"
537*44704f69SBart Van Assche             "    count       number of blocks to copy (def: device size)\n"
538*44704f69SBart Van Assche             "    dc          xcopy segment descriptor DC bit (default: 0)\n"
539*44704f69SBart Van Assche             "    fco         xcopy segment descriptor FCO bit (default: 0)\n"
540*44704f69SBart Van Assche             "    ibs         input block size (if given must be same as "
541*44704f69SBart Van Assche             "'bs=')\n"
542*44704f69SBart Van Assche             "    id_usage    sets list_id_usage field to hold (0), "
543*44704f69SBart Van Assche             "discard (2) or\n"
544*44704f69SBart Van Assche             "                disable (3)\n"
545*44704f69SBart Van Assche             "    if          file or device to read from (def: stdin)\n"
546*44704f69SBart Van Assche             "    iflag       comma separated list of flags applying to "
547*44704f69SBart Van Assche             "IFILE\n"
548*44704f69SBart Van Assche             "    list_id     sets list_id field to ID (default: 1 or 0)\n"
549*44704f69SBart Van Assche             "    obs         output block size (if given must be same as "
550*44704f69SBart Van Assche             "'bs=')\n"
551*44704f69SBart Van Assche             "    of          file or device to write to (def: stdout), "
552*44704f69SBart Van Assche             "OFILE of '.'\n");
553*44704f69SBart Van Assche     pr2serr("                treated as /dev/null\n"
554*44704f69SBart Van Assche             "    oflag       comma separated list of flags applying to "
555*44704f69SBart Van Assche             "OFILE\n"
556*44704f69SBart Van Assche             "    prio        set xcopy priority field to PRIO (def: 1)\n"
557*44704f69SBart Van Assche             "    seek        block position to start writing to OFILE\n"
558*44704f69SBart Van Assche             "    skip        block position to start reading from IFILE\n"
559*44704f69SBart Van Assche             "    time        0->no timing(def), 1->time plus calculate "
560*44704f69SBart Van Assche             "throughput\n"
561*44704f69SBart Van Assche             "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
562*44704f69SBart Van Assche             "etc\n"
563*44704f69SBart Van Assche             "    --help|-h   print out this usage message then exit\n"
564*44704f69SBart Van Assche             "    --on_dst    send XCOPY command to OFILE\n"
565*44704f69SBart Van Assche             "    --on_src    send XCOPY command to IFILE\n"
566*44704f69SBart Van Assche             "    --verbose|-v   same action as verbose=1\n"
567*44704f69SBart Van Assche             "    --version|-V   print version information then exit\n\n"
568*44704f69SBart Van Assche             "Copy from IFILE to OFILE, similar to dd command; "
569*44704f69SBart Van Assche             "but using the SCSI\nEXTENDED COPY (XCOPY(LID1)) command. For "
570*44704f69SBart Van Assche             "list of flags, use '-hh'.\n");
571*44704f69SBart Van Assche     return;
572*44704f69SBart Van Assche 
573*44704f69SBart Van Assche secondary_help:
574*44704f69SBart Van Assche     pr2serr("FLAGS:\n"
575*44704f69SBart Van Assche             "  append (o)     open OFILE in append mode\n"
576*44704f69SBart Van Assche             "  excl           open corresponding device with O_EXCL\n"
577*44704f69SBart Van Assche             "  flock          call flock(LOCK_EX|LOCK_NB)\n"
578*44704f69SBart Van Assche             "  null           does nothing, placeholder\n"
579*44704f69SBart Van Assche             "  pad            set xcopy data descriptor PAD bit on\n"
580*44704f69SBart Van Assche             "                 corresponding device\n"
581*44704f69SBart Van Assche             "  xcopy          send XCOPY command to corresponding device\n"
582*44704f69SBart Van Assche             "\n"
583*44704f69SBart Van Assche             "ENVIRONMENT VARIABLES:\n"
584*44704f69SBart Van Assche             "  XCOPY_TO_DST   send XCOPY command to OFILE (destination) "
585*44704f69SBart Van Assche             "if no other\n"
586*44704f69SBart Van Assche             "                 indication\n"
587*44704f69SBart Van Assche             "  XCOPY_TO_SRC   send XCOPY command to IFILE (source)\n"
588*44704f69SBart Van Assche            );
589*44704f69SBart Van Assche }
590*44704f69SBart Van Assche 
591*44704f69SBart Van Assche static int
scsi_encode_seg_desc(uint8_t * seg_desc,int seg_desc_type,int64_t num_blk,uint64_t src_lba,uint64_t dst_lba)592*44704f69SBart Van Assche scsi_encode_seg_desc(uint8_t *seg_desc, int seg_desc_type,
593*44704f69SBart Van Assche                      int64_t num_blk, uint64_t src_lba, uint64_t dst_lba)
594*44704f69SBart Van Assche {
595*44704f69SBart Van Assche     int seg_desc_len = 0;
596*44704f69SBart Van Assche 
597*44704f69SBart Van Assche     seg_desc[0] = (uint8_t)seg_desc_type;
598*44704f69SBart Van Assche     seg_desc[1] = 0x0;
599*44704f69SBart Van Assche     if (xcopy_flag_cat)
600*44704f69SBart Van Assche         seg_desc[1] |= 0x1;
601*44704f69SBart Van Assche     if (xcopy_flag_dc)
602*44704f69SBart Van Assche         seg_desc[1] |= 0x2;
603*44704f69SBart Van Assche     if (xcopy_flag_fco)
604*44704f69SBart Van Assche         seg_desc[1] |= 0x4;
605*44704f69SBart Van Assche     if (seg_desc_type == 0x02) {
606*44704f69SBart Van Assche         seg_desc_len = 0x18;
607*44704f69SBart Van Assche         seg_desc[4] = 0;
608*44704f69SBart Van Assche         seg_desc[5] = 0; /* Source target index */
609*44704f69SBart Van Assche         seg_desc[7] = 1; /* Destination target index */
610*44704f69SBart Van Assche         sg_put_unaligned_be16(num_blk, seg_desc + 10);
611*44704f69SBart Van Assche         sg_put_unaligned_be64(src_lba, seg_desc + 12);
612*44704f69SBart Van Assche         sg_put_unaligned_be64(dst_lba, seg_desc + 20);
613*44704f69SBart Van Assche     }
614*44704f69SBart Van Assche     sg_put_unaligned_be16(seg_desc_len, seg_desc + 2);
615*44704f69SBart Van Assche     return seg_desc_len + 4;
616*44704f69SBart Van Assche }
617*44704f69SBart Van Assche 
618*44704f69SBart Van Assche static int
scsi_extended_copy(int sg_fd,uint8_t list_id,uint8_t * src_desc,int src_desc_len,uint8_t * dst_desc,int dst_desc_len,int seg_desc_type,int64_t num_blk,uint64_t src_lba,uint64_t dst_lba)619*44704f69SBart Van Assche scsi_extended_copy(int sg_fd, uint8_t list_id,
620*44704f69SBart Van Assche                    uint8_t *src_desc, int src_desc_len,
621*44704f69SBart Van Assche                    uint8_t *dst_desc, int dst_desc_len,
622*44704f69SBart Van Assche                    int seg_desc_type, int64_t num_blk,
623*44704f69SBart Van Assche                    uint64_t src_lba, uint64_t dst_lba)
624*44704f69SBart Van Assche {
625*44704f69SBart Van Assche     uint8_t xcopyBuff[256];
626*44704f69SBart Van Assche     int desc_offset = 16;
627*44704f69SBart Van Assche     int seg_desc_len;
628*44704f69SBart Van Assche     int verb, res;
629*44704f69SBart Van Assche     char b[80];
630*44704f69SBart Van Assche 
631*44704f69SBart Van Assche     verb = (verbose > 1) ? (verbose - 2) : 0;
632*44704f69SBart Van Assche     memset(xcopyBuff, 0, 256);
633*44704f69SBart Van Assche     xcopyBuff[0] = list_id;
634*44704f69SBart Van Assche     xcopyBuff[1] = (list_id_usage << 3) | priority;
635*44704f69SBart Van Assche     xcopyBuff[2] = 0;
636*44704f69SBart Van Assche     xcopyBuff[3] = src_desc_len + dst_desc_len; /* Two target descriptors */
637*44704f69SBart Van Assche     memcpy(xcopyBuff + desc_offset, src_desc, src_desc_len);
638*44704f69SBart Van Assche     desc_offset += src_desc_len;
639*44704f69SBart Van Assche     memcpy(xcopyBuff + desc_offset, dst_desc, dst_desc_len);
640*44704f69SBart Van Assche     desc_offset += dst_desc_len;
641*44704f69SBart Van Assche     seg_desc_len = scsi_encode_seg_desc(xcopyBuff + desc_offset,
642*44704f69SBart Van Assche                                         seg_desc_type, num_blk,
643*44704f69SBart Van Assche                                         src_lba, dst_lba);
644*44704f69SBart Van Assche     xcopyBuff[11] = seg_desc_len; /* One segment descriptor */
645*44704f69SBart Van Assche     desc_offset += seg_desc_len;
646*44704f69SBart Van Assche     /* set noisy so if a UA happens it will be printed to stderr */
647*44704f69SBart Van Assche     res = sg_ll_3party_copy_out(sg_fd, SA_XCOPY_LID1, list_id,
648*44704f69SBart Van Assche                                 DEF_GROUP_NUM, DEF_3PC_OUT_TIMEOUT,
649*44704f69SBart Van Assche                                 xcopyBuff, desc_offset, true, verb);
650*44704f69SBart Van Assche     if (res) {
651*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verb);
652*44704f69SBart Van Assche         pr2serr("Xcopy(LID1): %s\n", b);
653*44704f69SBart Van Assche     }
654*44704f69SBart Van Assche     return res;
655*44704f69SBart Van Assche }
656*44704f69SBart Van Assche 
657*44704f69SBart Van Assche /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
658*44704f69SBart Van Assche static int
scsi_read_capacity(struct xcopy_fp_t * xfp)659*44704f69SBart Van Assche scsi_read_capacity(struct xcopy_fp_t *xfp)
660*44704f69SBart Van Assche {
661*44704f69SBart Van Assche     int res;
662*44704f69SBart Van Assche     unsigned int ui;
663*44704f69SBart Van Assche     uint8_t rcBuff[RCAP16_REPLY_LEN];
664*44704f69SBart Van Assche     int verb;
665*44704f69SBart Van Assche     char b[80];
666*44704f69SBart Van Assche 
667*44704f69SBart Van Assche     verb = (verbose ? verbose - 1: 0);
668*44704f69SBart Van Assche     res = sg_ll_readcap_10(xfp->sg_fd, false /* pmi */, 0, rcBuff,
669*44704f69SBart Van Assche                            READ_CAP_REPLY_LEN, true, verb);
670*44704f69SBart Van Assche     if (0 != res) {
671*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verb);
672*44704f69SBart Van Assche         pr2serr("Read capacity(10): %s\n", b);
673*44704f69SBart Van Assche         return res;
674*44704f69SBart Van Assche     }
675*44704f69SBart Van Assche 
676*44704f69SBart Van Assche     if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
677*44704f69SBart Van Assche         (0xff == rcBuff[3])) {
678*44704f69SBart Van Assche         uint64_t ls;
679*44704f69SBart Van Assche 
680*44704f69SBart Van Assche         res = sg_ll_readcap_16(xfp->sg_fd, false /* pmi */, 0, rcBuff,
681*44704f69SBart Van Assche                                RCAP16_REPLY_LEN, true, verb);
682*44704f69SBart Van Assche         if (0 != res) {
683*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, verb);
684*44704f69SBart Van Assche             pr2serr("Read capacity(16): %s\n", b);
685*44704f69SBart Van Assche             return res;
686*44704f69SBart Van Assche         }
687*44704f69SBart Van Assche         ls = sg_get_unaligned_be64(rcBuff + 0);
688*44704f69SBart Van Assche         xfp->num_sect = (int64_t)(ls + 1);
689*44704f69SBart Van Assche         xfp->sect_sz = sg_get_unaligned_be32(rcBuff + 8);
690*44704f69SBart Van Assche     } else {
691*44704f69SBart Van Assche         ui = sg_get_unaligned_be32(rcBuff + 0);
692*44704f69SBart Van Assche         /* take care not to sign extend values > 0x7fffffff */
693*44704f69SBart Van Assche         xfp->num_sect = (int64_t)ui + 1;
694*44704f69SBart Van Assche         xfp->sect_sz = sg_get_unaligned_be32(rcBuff + 4);
695*44704f69SBart Van Assche     }
696*44704f69SBart Van Assche     if (verbose)
697*44704f69SBart Van Assche         pr2serr("    %s: number of blocks=%" PRId64 " [0x%" PRIx64 "], block "
698*44704f69SBart Van Assche                 "size=%d\n", xfp->fname, xfp->num_sect, xfp->num_sect,
699*44704f69SBart Van Assche                 xfp->sect_sz);
700*44704f69SBart Van Assche     return 0;
701*44704f69SBart Van Assche }
702*44704f69SBart Van Assche 
703*44704f69SBart Van Assche static int
scsi_operating_parameter(struct xcopy_fp_t * xfp,int is_target)704*44704f69SBart Van Assche scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
705*44704f69SBart Van Assche {
706*44704f69SBart Van Assche     bool valid = false;
707*44704f69SBart Van Assche     int res, ftype, snlid, verb;
708*44704f69SBart Van Assche     uint32_t rcBuffLen = 256, len, n, td_list = 0;
709*44704f69SBart Van Assche     uint32_t num, max_target_num, max_segment_num, max_segment_len;
710*44704f69SBart Van Assche     uint32_t max_desc_len, max_inline_data, held_data_limit;
711*44704f69SBart Van Assche     uint8_t rcBuff[256];
712*44704f69SBart Van Assche     char b[80];
713*44704f69SBart Van Assche 
714*44704f69SBart Van Assche     verb = (verbose ? verbose - 1: 0);
715*44704f69SBart Van Assche     ftype = xfp->sg_type;
716*44704f69SBart Van Assche     if (FT_SG & ftype) {
717*44704f69SBart Van Assche         if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* direct-access or RBC */
718*44704f69SBart Van Assche             ftype |= FT_BLOCK;
719*44704f69SBart Van Assche         else if (0x1 == xfp->pdt)
720*44704f69SBart Van Assche             ftype |= FT_ST;
721*44704f69SBart Van Assche     }
722*44704f69SBart Van Assche     res = sg_ll_receive_copy_results(xfp->sg_fd, SA_COPY_OP_PARAMS, 0, rcBuff,
723*44704f69SBart Van Assche                                      rcBuffLen, true, verb);
724*44704f69SBart Van Assche     if (0 != res) {
725*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verb);
726*44704f69SBart Van Assche         pr2serr("Xcopy operating parameters: %s\n", b);
727*44704f69SBart Van Assche         return -res;
728*44704f69SBart Van Assche     }
729*44704f69SBart Van Assche 
730*44704f69SBart Van Assche     len = sg_get_unaligned_be32(rcBuff + 0);
731*44704f69SBart Van Assche     if (len > rcBuffLen) {
732*44704f69SBart Van Assche         pr2serr("  <<report len %d > %d too long for internal buffer, output "
733*44704f69SBart Van Assche                 "truncated\n", len, rcBuffLen);
734*44704f69SBart Van Assche     }
735*44704f69SBart Van Assche     if (verbose > 2) {
736*44704f69SBart Van Assche         pr2serr("\nOutput response in hex:\n");
737*44704f69SBart Van Assche         hex2stderr(rcBuff, len, 1);
738*44704f69SBart Van Assche     }
739*44704f69SBart Van Assche     snlid = rcBuff[4] & 0x1;
740*44704f69SBart Van Assche     max_target_num = sg_get_unaligned_be16(rcBuff + 8);
741*44704f69SBart Van Assche     max_segment_num = sg_get_unaligned_be16(rcBuff + 10);
742*44704f69SBart Van Assche     max_desc_len = sg_get_unaligned_be32(rcBuff + 12);
743*44704f69SBart Van Assche     max_segment_len = sg_get_unaligned_be32(rcBuff + 16);
744*44704f69SBart Van Assche     xfp->max_bytes = max_segment_len ? max_segment_len : UINT32_MAX;
745*44704f69SBart Van Assche     max_inline_data = sg_get_unaligned_be32(rcBuff + 20);
746*44704f69SBart Van Assche     if (verbose) {
747*44704f69SBart Van Assche         pr2serr(" >> %s response:\n", rec_copy_op_params_str);
748*44704f69SBart Van Assche         pr2serr("    Support No List IDentifier (SNLID): %d\n", snlid);
749*44704f69SBart Van Assche         pr2serr("    Maximum target descriptor count: %u\n",
750*44704f69SBart Van Assche                 (unsigned int)max_target_num);
751*44704f69SBart Van Assche         pr2serr("    Maximum segment descriptor count: %u\n",
752*44704f69SBart Van Assche                 (unsigned int)max_segment_num);
753*44704f69SBart Van Assche         pr2serr("    Maximum descriptor list length: %u\n",
754*44704f69SBart Van Assche                 (unsigned int)max_desc_len);
755*44704f69SBart Van Assche         pr2serr("    Maximum segment length: %u\n",
756*44704f69SBart Van Assche                 (unsigned int)max_segment_len);
757*44704f69SBart Van Assche         pr2serr("    Maximum inline data length: %u\n",
758*44704f69SBart Van Assche                 (unsigned int)max_inline_data);
759*44704f69SBart Van Assche     }
760*44704f69SBart Van Assche     held_data_limit = sg_get_unaligned_be32(rcBuff + 24);
761*44704f69SBart Van Assche     if (list_id_usage < 0) {
762*44704f69SBart Van Assche         if (!held_data_limit)
763*44704f69SBart Van Assche             list_id_usage = 2;
764*44704f69SBart Van Assche         else
765*44704f69SBart Van Assche             list_id_usage = 0;
766*44704f69SBart Van Assche     }
767*44704f69SBart Van Assche     if (verbose) {
768*44704f69SBart Van Assche         pr2serr("    Held data limit: %u (list_id_usage: %d)\n",
769*44704f69SBart Van Assche                 (unsigned int)held_data_limit, list_id_usage);
770*44704f69SBart Van Assche         num = sg_get_unaligned_be32(rcBuff + 28);
771*44704f69SBart Van Assche         pr2serr("    Maximum stream device transfer size: %u\n",
772*44704f69SBart Van Assche                 (unsigned int)num);
773*44704f69SBart Van Assche         pr2serr("    Maximum concurrent copies: %u\n", rcBuff[36]);
774*44704f69SBart Van Assche         if (rcBuff[37] > 30)
775*44704f69SBart Van Assche             pr2serr("    Data segment granularity: 2**%u bytes\n",
776*44704f69SBart Van Assche                     rcBuff[37]);
777*44704f69SBart Van Assche         else
778*44704f69SBart Van Assche             pr2serr("    Data segment granularity: %u bytes\n",
779*44704f69SBart Van Assche                     1 << rcBuff[37]);
780*44704f69SBart Van Assche         if (rcBuff[38] > 30)
781*44704f69SBart Van Assche             pr2serr("    Inline data granularity: 2**%u bytes\n", rcBuff[38]);
782*44704f69SBart Van Assche         else
783*44704f69SBart Van Assche             pr2serr("    Inline data granularity: %u bytes\n",
784*44704f69SBart Van Assche                     1 << rcBuff[38]);
785*44704f69SBart Van Assche         if (rcBuff[39] > 30)
786*44704f69SBart Van Assche             pr2serr("    Held data granularity: 2**%u bytes\n",
787*44704f69SBart Van Assche                     1 << rcBuff[39]);
788*44704f69SBart Van Assche         else
789*44704f69SBart Van Assche             pr2serr("    Held data granularity: %u bytes\n", 1 << rcBuff[39]);
790*44704f69SBart Van Assche 
791*44704f69SBart Van Assche         pr2serr("    Implemented descriptor list:\n");
792*44704f69SBart Van Assche     }
793*44704f69SBart Van Assche     xfp->min_bytes = 1 << rcBuff[37];
794*44704f69SBart Van Assche 
795*44704f69SBart Van Assche     for (n = 0; n < rcBuff[43]; n++) {
796*44704f69SBart Van Assche         switch(rcBuff[44 + n]) {
797*44704f69SBart Van Assche         case 0x00: /* copy block to stream device */
798*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
799*44704f69SBart Van Assche                 valid = true;
800*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
801*44704f69SBart Van Assche                 valid = true;
802*44704f69SBart Van Assche             if (verbose)
803*44704f69SBart Van Assche                 pr2serr("        Copy Block to Stream device\n");
804*44704f69SBart Van Assche             break;
805*44704f69SBart Van Assche         case 0x01: /* copy stream to block device */
806*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
807*44704f69SBart Van Assche                 valid = true;
808*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
809*44704f69SBart Van Assche                 valid = true;
810*44704f69SBart Van Assche             if (verbose)
811*44704f69SBart Van Assche                 pr2serr("        Copy Stream to Block device\n");
812*44704f69SBart Van Assche             break;
813*44704f69SBart Van Assche         case 0x02: /* copy block to block device */
814*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
815*44704f69SBart Van Assche                 valid = true;
816*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
817*44704f69SBart Van Assche                 valid = true;
818*44704f69SBart Van Assche             if (verbose)
819*44704f69SBart Van Assche                 pr2serr("        Copy Block to Block device\n");
820*44704f69SBart Van Assche             break;
821*44704f69SBart Van Assche         case 0x03: /* copy stream to stream device */
822*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
823*44704f69SBart Van Assche                 valid = true;
824*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
825*44704f69SBart Van Assche                 valid = true;
826*44704f69SBart Van Assche             if (verbose)
827*44704f69SBart Van Assche                 pr2serr("        Copy Stream to Stream device\n");
828*44704f69SBart Van Assche             break;
829*44704f69SBart Van Assche         case 0x04: /* copy inline data to stream device */
830*44704f69SBart Van Assche             if (!is_target && (ftype & FT_OTHER))
831*44704f69SBart Van Assche                 valid = true;
832*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
833*44704f69SBart Van Assche                 valid = true;
834*44704f69SBart Van Assche             if (verbose)
835*44704f69SBart Van Assche                 pr2serr("        Copy inline data to Stream device\n");
836*44704f69SBart Van Assche             break;
837*44704f69SBart Van Assche         case 0x05: /* copy embedded data to stream device */
838*44704f69SBart Van Assche             if (!is_target && (ftype & FT_OTHER))
839*44704f69SBart Van Assche                 valid = true;
840*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
841*44704f69SBart Van Assche                 valid = true;
842*44704f69SBart Van Assche             if (verbose)
843*44704f69SBart Van Assche                 pr2serr("        Copy embedded data to Stream device\n");
844*44704f69SBart Van Assche             break;
845*44704f69SBart Van Assche         case 0x06: /* Read from stream device and discard */
846*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
847*44704f69SBart Van Assche                 valid = true;
848*44704f69SBart Van Assche             if (is_target && (ftype & FT_DEV_NULL))
849*44704f69SBart Van Assche                 valid = true;
850*44704f69SBart Van Assche             if (verbose)
851*44704f69SBart Van Assche                 pr2serr("        Read from stream device and discard\n");
852*44704f69SBart Van Assche             break;
853*44704f69SBart Van Assche         case 0x07: /* Verify block or stream device operation */
854*44704f69SBart Van Assche             if (!is_target && (ftype & (FT_ST | FT_BLOCK)))
855*44704f69SBart Van Assche                 valid = true;
856*44704f69SBart Van Assche             if (is_target && (ftype & (FT_ST | FT_BLOCK)))
857*44704f69SBart Van Assche                 valid = true;
858*44704f69SBart Van Assche             if (verbose)
859*44704f69SBart Van Assche                 pr2serr("        Verify block or stream device operation\n");
860*44704f69SBart Van Assche             break;
861*44704f69SBart Van Assche         case 0x08: /* copy block device with offset to stream device */
862*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
863*44704f69SBart Van Assche                 valid = true;
864*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
865*44704f69SBart Van Assche                 valid = true;
866*44704f69SBart Van Assche             if (verbose)
867*44704f69SBart Van Assche                 pr2serr("        Copy block device with offset to stream "
868*44704f69SBart Van Assche                        "device\n");
869*44704f69SBart Van Assche             break;
870*44704f69SBart Van Assche         case 0x09: /* copy stream device to block device with offset */
871*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
872*44704f69SBart Van Assche                 valid = true;
873*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
874*44704f69SBart Van Assche                 valid = true;
875*44704f69SBart Van Assche             if (verbose)
876*44704f69SBart Van Assche                 pr2serr("        Copy stream device to block device with "
877*44704f69SBart Van Assche                        "offset\n");
878*44704f69SBart Van Assche             break;
879*44704f69SBart Van Assche         case 0x0a: /* copy block device with offset to block device with
880*44704f69SBart Van Assche                     * offset */
881*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
882*44704f69SBart Van Assche                 valid = true;
883*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
884*44704f69SBart Van Assche                 valid = true;
885*44704f69SBart Van Assche             if (verbose)
886*44704f69SBart Van Assche                 pr2serr("        Copy block device with offset to block "
887*44704f69SBart Van Assche                        "device with offset\n");
888*44704f69SBart Van Assche             break;
889*44704f69SBart Van Assche         case 0x0b: /* copy block device to stream device and hold data */
890*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
891*44704f69SBart Van Assche                 valid = true;
892*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
893*44704f69SBart Van Assche                 valid = true;
894*44704f69SBart Van Assche             if (verbose)
895*44704f69SBart Van Assche                 pr2serr("        Copy block device to stream device and hold "
896*44704f69SBart Van Assche                        "data\n");
897*44704f69SBart Van Assche             break;
898*44704f69SBart Van Assche         case 0x0c: /* copy stream device to block device and hold data */
899*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
900*44704f69SBart Van Assche                 valid = true;
901*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
902*44704f69SBart Van Assche                 valid = true;
903*44704f69SBart Van Assche             if (verbose)
904*44704f69SBart Van Assche                 pr2serr("        Copy stream device to block device and hold "
905*44704f69SBart Van Assche                        "data\n");
906*44704f69SBart Van Assche             break;
907*44704f69SBart Van Assche         case 0x0d: /* copy block device to block device and hold data */
908*44704f69SBart Van Assche             if (!is_target && (ftype & FT_BLOCK))
909*44704f69SBart Van Assche                 valid = true;
910*44704f69SBart Van Assche             if (is_target && (ftype & FT_BLOCK))
911*44704f69SBart Van Assche                 valid = true;
912*44704f69SBart Van Assche             if (verbose)
913*44704f69SBart Van Assche                 pr2serr("        Copy block device to block device and hold "
914*44704f69SBart Van Assche                        "data\n");
915*44704f69SBart Van Assche             break;
916*44704f69SBart Van Assche         case 0x0e: /* copy stream device to stream device and hold data */
917*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
918*44704f69SBart Van Assche                 valid = true;
919*44704f69SBart Van Assche             if (is_target && (ftype & FT_ST))
920*44704f69SBart Van Assche                 valid = true;
921*44704f69SBart Van Assche             if (verbose)
922*44704f69SBart Van Assche                 pr2serr("        Copy block device to block device and hold "
923*44704f69SBart Van Assche                        "data\n");
924*44704f69SBart Van Assche             break;
925*44704f69SBart Van Assche         case 0x0f: /* read from stream device and hold data */
926*44704f69SBart Van Assche             if (!is_target && (ftype & FT_ST))
927*44704f69SBart Van Assche                 valid = true;
928*44704f69SBart Van Assche             if (is_target && (ftype & FT_DEV_NULL))
929*44704f69SBart Van Assche                 valid = true;
930*44704f69SBart Van Assche             if (verbose)
931*44704f69SBart Van Assche                 pr2serr("        Read from stream device and hold data\n");
932*44704f69SBart Van Assche             break;
933*44704f69SBart Van Assche         case 0xe0: /* FC N_Port_Name */
934*44704f69SBart Van Assche             if (verbose)
935*44704f69SBart Van Assche                 pr2serr("        FC N_Port_Name target descriptor\n");
936*44704f69SBart Van Assche             td_list |= TD_FC_WWPN;
937*44704f69SBart Van Assche             break;
938*44704f69SBart Van Assche         case 0xe1: /* FC Port_ID */
939*44704f69SBart Van Assche             if (verbose)
940*44704f69SBart Van Assche                 pr2serr("        FC Port_ID target descriptor\n");
941*44704f69SBart Van Assche             td_list |= TD_FC_PORT;
942*44704f69SBart Van Assche             break;
943*44704f69SBart Van Assche         case 0xe2: /* FC N_Port_ID with N_Port_Name checking */
944*44704f69SBart Van Assche             if (verbose)
945*44704f69SBart Van Assche                 pr2serr("        FC N_Port_ID with N_Port_Name target "
946*44704f69SBart Van Assche                        "descriptor\n");
947*44704f69SBart Van Assche             td_list |= TD_FC_WWPN_AND_PORT;
948*44704f69SBart Van Assche             break;
949*44704f69SBart Van Assche         case 0xe3: /* Parallel Interface T_L  */
950*44704f69SBart Van Assche             if (verbose)
951*44704f69SBart Van Assche                 pr2serr("        SPI T_L target descriptor\n");
952*44704f69SBart Van Assche             td_list |= TD_SPI;
953*44704f69SBart Van Assche             break;
954*44704f69SBart Van Assche         case 0xe4: /* identification descriptor */
955*44704f69SBart Van Assche             if (verbose)
956*44704f69SBart Van Assche                 pr2serr("        Identification target descriptor\n");
957*44704f69SBart Van Assche             td_list |= TD_VPD;
958*44704f69SBart Van Assche             break;
959*44704f69SBart Van Assche         case 0xe5: /* IPv4  */
960*44704f69SBart Van Assche             if (verbose)
961*44704f69SBart Van Assche                 pr2serr("        IPv4 target descriptor\n");
962*44704f69SBart Van Assche             td_list |= TD_IPV4;
963*44704f69SBart Van Assche             break;
964*44704f69SBart Van Assche         case 0xe6: /* Alias */
965*44704f69SBart Van Assche             if (verbose)
966*44704f69SBart Van Assche                 pr2serr("        Alias target descriptor\n");
967*44704f69SBart Van Assche             td_list |= TD_ALIAS;
968*44704f69SBart Van Assche             break;
969*44704f69SBart Van Assche         case 0xe7: /* RDMA */
970*44704f69SBart Van Assche             if (verbose)
971*44704f69SBart Van Assche                 pr2serr("        RDMA target descriptor\n");
972*44704f69SBart Van Assche             td_list |= TD_RDMA;
973*44704f69SBart Van Assche             break;
974*44704f69SBart Van Assche         case 0xe8: /* FireWire */
975*44704f69SBart Van Assche             if (verbose)
976*44704f69SBart Van Assche                 pr2serr("        IEEE 1394 target descriptor\n");
977*44704f69SBart Van Assche             td_list |= TD_FW;
978*44704f69SBart Van Assche             break;
979*44704f69SBart Van Assche         case 0xe9: /* SAS */
980*44704f69SBart Van Assche             if (verbose)
981*44704f69SBart Van Assche                 pr2serr("        SAS target descriptor\n");
982*44704f69SBart Van Assche             td_list |= TD_SAS;
983*44704f69SBart Van Assche             break;
984*44704f69SBart Van Assche         case 0xea: /* IPv6 */
985*44704f69SBart Van Assche             if (verbose)
986*44704f69SBart Van Assche                 pr2serr("        IPv6 target descriptor\n");
987*44704f69SBart Van Assche             td_list |= TD_IPV6;
988*44704f69SBart Van Assche             break;
989*44704f69SBart Van Assche         case 0xeb: /* IP Copy Service */
990*44704f69SBart Van Assche             if (verbose)
991*44704f69SBart Van Assche                 pr2serr("        IP Copy Service target descriptor\n");
992*44704f69SBart Van Assche             td_list |= TD_IP_COPY_SERVICE;
993*44704f69SBart Van Assche             break;
994*44704f69SBart Van Assche         case 0xfe: /* ROD */
995*44704f69SBart Van Assche             if (verbose)
996*44704f69SBart Van Assche                 pr2serr("        ROD target descriptor\n");
997*44704f69SBart Van Assche             td_list |= TD_ROD;
998*44704f69SBart Van Assche             break;
999*44704f69SBart Van Assche         default:
1000*44704f69SBart Van Assche             pr2serr(">> Unhandled target descriptor 0x%02x\n",
1001*44704f69SBart Van Assche                     rcBuff[44 + n]);
1002*44704f69SBart Van Assche             break;
1003*44704f69SBart Van Assche         }
1004*44704f69SBart Van Assche     }
1005*44704f69SBart Van Assche     if (! valid) {
1006*44704f69SBart Van Assche         pr2serr(">> no matching target descriptor supported\n");
1007*44704f69SBart Van Assche         td_list = 0;
1008*44704f69SBart Van Assche     }
1009*44704f69SBart Van Assche     return td_list;
1010*44704f69SBart Van Assche }
1011*44704f69SBart Van Assche 
1012*44704f69SBart Van Assche static void
decode_designation_descriptor(const uint8_t * bp,int i_len)1013*44704f69SBart Van Assche decode_designation_descriptor(const uint8_t * bp, int i_len)
1014*44704f69SBart Van Assche {
1015*44704f69SBart Van Assche     char c[2048];
1016*44704f69SBart Van Assche 
1017*44704f69SBart Van Assche     sg_get_designation_descriptor_str(NULL, bp, i_len, 1, verbose,
1018*44704f69SBart Van Assche                                       sizeof(c), c);
1019*44704f69SBart Van Assche     pr2serr("%s", c);
1020*44704f69SBart Van Assche }
1021*44704f69SBart Van Assche 
1022*44704f69SBart Van Assche static int
desc_from_vpd_id(int sg_fd,uint8_t * desc,int desc_len,unsigned int block_size,bool pad)1023*44704f69SBart Van Assche desc_from_vpd_id(int sg_fd, uint8_t *desc, int desc_len,
1024*44704f69SBart Van Assche                  unsigned int block_size, bool pad)
1025*44704f69SBart Van Assche {
1026*44704f69SBart Van Assche     int res, verb;
1027*44704f69SBart Van Assche     uint8_t rcBuff[256], *bp, *best = NULL;
1028*44704f69SBart Van Assche     unsigned int len = 254;
1029*44704f69SBart Van Assche     int off = -1, i_len, best_len = 0, assoc, desig, f_desig = 0;
1030*44704f69SBart Van Assche     char b[80];
1031*44704f69SBart Van Assche 
1032*44704f69SBart Van Assche     verb = (verbose ? verbose - 1: 0);
1033*44704f69SBart Van Assche     memset(rcBuff, 0xff, len);
1034*44704f69SBart Van Assche     res = sg_ll_inquiry(sg_fd, false, true /* evpd */, VPD_DEVICE_ID, rcBuff,
1035*44704f69SBart Van Assche                         4, true, verb);
1036*44704f69SBart Van Assche     if (0 != res) {
1037*44704f69SBart Van Assche         if (SG_LIB_CAT_ILLEGAL_REQ == res)
1038*44704f69SBart Van Assche             pr2serr("Device identification VPD page not found\n");
1039*44704f69SBart Van Assche         else {
1040*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, verbose);
1041*44704f69SBart Van Assche             pr2serr("VPD inquiry (Device ID): %s\n", b);
1042*44704f69SBart Van Assche             pr2serr("   try again with '-vv'\n");
1043*44704f69SBart Van Assche         }
1044*44704f69SBart Van Assche         return res;
1045*44704f69SBart Van Assche     } else if (rcBuff[1] != VPD_DEVICE_ID) {
1046*44704f69SBart Van Assche         pr2serr("invalid VPD response\n");
1047*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
1048*44704f69SBart Van Assche     }
1049*44704f69SBart Van Assche     len = sg_get_unaligned_be16(rcBuff + 2) + 4;
1050*44704f69SBart Van Assche     res = sg_ll_inquiry(sg_fd, false, true, VPD_DEVICE_ID, rcBuff, len, true,
1051*44704f69SBart Van Assche                         verb);
1052*44704f69SBart Van Assche     if (0 != res) {
1053*44704f69SBart Van Assche         sg_get_category_sense_str(res, sizeof(b), b, verbose);
1054*44704f69SBart Van Assche         pr2serr("VPD inquiry (Device ID): %s\n", b);
1055*44704f69SBart Van Assche         return res;
1056*44704f69SBart Van Assche     } else if (rcBuff[1] != VPD_DEVICE_ID) {
1057*44704f69SBart Van Assche         pr2serr("invalid VPD response\n");
1058*44704f69SBart Van Assche         return SG_LIB_CAT_MALFORMED;
1059*44704f69SBart Van Assche     }
1060*44704f69SBart Van Assche     if (verbose > 2) {
1061*44704f69SBart Van Assche         pr2serr("Output response in hex:\n");
1062*44704f69SBart Van Assche         hex2stderr(rcBuff, len, 1);
1063*44704f69SBart Van Assche     }
1064*44704f69SBart Van Assche 
1065*44704f69SBart Van Assche     while (sg_vpd_dev_id_iter(rcBuff + 4, len - 4, &off, 0, -1, -1) == 0) {
1066*44704f69SBart Van Assche         bp = rcBuff + 4 + off;
1067*44704f69SBart Van Assche         i_len = bp[3];
1068*44704f69SBart Van Assche         if (((unsigned int)off + i_len + 4) > len) {
1069*44704f69SBart Van Assche             pr2serr("    VPD page error: designator length %d longer "
1070*44704f69SBart Van Assche                     "than\n     remaining response length=%d\n", i_len,
1071*44704f69SBart Van Assche                     (len - off));
1072*44704f69SBart Van Assche             return SG_LIB_CAT_MALFORMED;
1073*44704f69SBart Van Assche         }
1074*44704f69SBart Van Assche         assoc = ((bp[1] >> 4) & 0x3);
1075*44704f69SBart Van Assche         desig = (bp[1] & 0xf);
1076*44704f69SBart Van Assche         if (verbose > 2)
1077*44704f69SBart Van Assche             pr2serr("    Desc %d: assoc %u desig %u len %d\n", off, assoc,
1078*44704f69SBart Van Assche                     desig, i_len);
1079*44704f69SBart Van Assche         /* Identification descriptor's Designator length must be <= 20. */
1080*44704f69SBart Van Assche         if (i_len > 20)
1081*44704f69SBart Van Assche             continue;
1082*44704f69SBart Van Assche         if (desig == /*NAA=*/3) {
1083*44704f69SBart Van Assche             best = bp;
1084*44704f69SBart Van Assche             best_len = i_len;
1085*44704f69SBart Van Assche             break;
1086*44704f69SBart Van Assche         }
1087*44704f69SBart Van Assche         if (desig == /*EUI64=*/2) {
1088*44704f69SBart Van Assche             if (!best || f_desig < 2) {
1089*44704f69SBart Van Assche                 best = bp;
1090*44704f69SBart Van Assche                 best_len = i_len;
1091*44704f69SBart Van Assche                 f_desig = 2;
1092*44704f69SBart Van Assche             }
1093*44704f69SBart Van Assche         } else if (desig == /*T10*/1) {
1094*44704f69SBart Van Assche             if (!best || f_desig == 0) {
1095*44704f69SBart Van Assche                 best = bp;
1096*44704f69SBart Van Assche                 best_len = i_len;
1097*44704f69SBart Van Assche                 f_desig = desig;
1098*44704f69SBart Van Assche             }
1099*44704f69SBart Van Assche         } else if (desig == /*vend.spec.=*/0) {
1100*44704f69SBart Van Assche             if (!best) {
1101*44704f69SBart Van Assche                 best = bp;
1102*44704f69SBart Van Assche                 best_len = i_len;
1103*44704f69SBart Van Assche                 f_desig = desig;
1104*44704f69SBart Van Assche             }
1105*44704f69SBart Van Assche         }
1106*44704f69SBart Van Assche     }
1107*44704f69SBart Van Assche     if (best) {
1108*44704f69SBart Van Assche         if (verbose)
1109*44704f69SBart Van Assche             decode_designation_descriptor(best, best_len);
1110*44704f69SBart Van Assche         if (best_len + 4 < desc_len) {
1111*44704f69SBart Van Assche             memset(desc, 0, 32);
1112*44704f69SBart Van Assche             desc[0] = 0xe4; /* Identification Descriptor */
1113*44704f69SBart Van Assche             memcpy(desc + 4, best, best_len + 4);
1114*44704f69SBart Van Assche             desc[4] &= 0x0f; /* code set */
1115*44704f69SBart Van Assche             desc[5] &= 0x3f; /* association and designator type */
1116*44704f69SBart Van Assche             if (pad)
1117*44704f69SBart Van Assche                 desc[28] = 0x4;
1118*44704f69SBart Van Assche             sg_put_unaligned_be24((uint32_t)block_size, desc + 29);
1119*44704f69SBart Van Assche             if (verbose > 3) {
1120*44704f69SBart Van Assche                 pr2serr("Descriptor in hex (bs %d):\n", block_size);
1121*44704f69SBart Van Assche                 hex2stderr(desc, 32, 1);
1122*44704f69SBart Van Assche             }
1123*44704f69SBart Van Assche             return 32;
1124*44704f69SBart Van Assche         }
1125*44704f69SBart Van Assche         return  best_len + 8;
1126*44704f69SBart Van Assche     }
1127*44704f69SBart Van Assche     return 0;
1128*44704f69SBart Van Assche }
1129*44704f69SBart Van Assche 
1130*44704f69SBart Van Assche static void
calc_duration_throughput(int contin)1131*44704f69SBart Van Assche calc_duration_throughput(int contin)
1132*44704f69SBart Van Assche {
1133*44704f69SBart Van Assche     struct timeval end_tm, res_tm;
1134*44704f69SBart Van Assche     double a, b;
1135*44704f69SBart Van Assche     int64_t blks;
1136*44704f69SBart Van Assche 
1137*44704f69SBart Van Assche     if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
1138*44704f69SBart Van Assche         blks = (in_full > out_full) ? in_full : out_full;
1139*44704f69SBart Van Assche         gettimeofday(&end_tm, NULL);
1140*44704f69SBart Van Assche         res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
1141*44704f69SBart Van Assche         res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
1142*44704f69SBart Van Assche         if (res_tm.tv_usec < 0) {
1143*44704f69SBart Van Assche             --res_tm.tv_sec;
1144*44704f69SBart Van Assche             res_tm.tv_usec += 1000000;
1145*44704f69SBart Van Assche         }
1146*44704f69SBart Van Assche         a = res_tm.tv_sec;
1147*44704f69SBart Van Assche         a += (0.000001 * res_tm.tv_usec);
1148*44704f69SBart Van Assche         b = (double)blk_sz * blks;
1149*44704f69SBart Van Assche         pr2serr("time to transfer data%s: %d.%06d secs",
1150*44704f69SBart Van Assche                 (contin ? " so far" : ""), (int)res_tm.tv_sec,
1151*44704f69SBart Van Assche                 (int)res_tm.tv_usec);
1152*44704f69SBart Van Assche         if ((a > 0.00001) && (b > 511))
1153*44704f69SBart Van Assche             pr2serr(" at %.2f MB/sec\n", b / (a * 1000000.0));
1154*44704f69SBart Van Assche         else
1155*44704f69SBart Van Assche             pr2serr("\n");
1156*44704f69SBart Van Assche     }
1157*44704f69SBart Van Assche }
1158*44704f69SBart Van Assche 
1159*44704f69SBart Van Assche /* Process arguments given to 'iflag=" or 'oflag=" options. Returns 0
1160*44704f69SBart Van Assche  * on success, 1 on error. */
1161*44704f69SBart Van Assche static int
process_flags(const char * arg,struct xcopy_fp_t * fp)1162*44704f69SBart Van Assche process_flags(const char * arg, struct xcopy_fp_t * fp)
1163*44704f69SBart Van Assche {
1164*44704f69SBart Van Assche     char buff[256];
1165*44704f69SBart Van Assche     char * cp;
1166*44704f69SBart Van Assche     char * np;
1167*44704f69SBart Van Assche 
1168*44704f69SBart Van Assche     strncpy(buff, arg, sizeof(buff) - 1);
1169*44704f69SBart Van Assche     buff[sizeof(buff) - 1] = '\0';
1170*44704f69SBart Van Assche     if ('\0' == buff[0]) {
1171*44704f69SBart Van Assche         pr2serr("no flag found\n");
1172*44704f69SBart Van Assche         return 1;
1173*44704f69SBart Van Assche     }
1174*44704f69SBart Van Assche     cp = buff;
1175*44704f69SBart Van Assche     do {
1176*44704f69SBart Van Assche         np = strchr(cp, ',');
1177*44704f69SBart Van Assche         if (np)
1178*44704f69SBart Van Assche             *np++ = '\0';
1179*44704f69SBart Van Assche         if (0 == strcmp(cp, "append"))
1180*44704f69SBart Van Assche             fp->append = true;
1181*44704f69SBart Van Assche         else if (0 == strcmp(cp, "excl"))
1182*44704f69SBart Van Assche             fp->excl = true;
1183*44704f69SBart Van Assche         else if (0 == strcmp(cp, "flock"))
1184*44704f69SBart Van Assche             fp->flock = true;
1185*44704f69SBart Van Assche         else if (0 == strcmp(cp, "null"))
1186*44704f69SBart Van Assche             ;
1187*44704f69SBart Van Assche         else if (0 == strcmp(cp, "pad"))
1188*44704f69SBart Van Assche             fp->pad = true;
1189*44704f69SBart Van Assche         else if (0 == strcmp(cp, "xcopy"))
1190*44704f69SBart Van Assche             fp->xcopy_given = true;   /* for ddpt compatibility */
1191*44704f69SBart Van Assche         else {
1192*44704f69SBart Van Assche             pr2serr("unrecognised flag: %s\n", cp);
1193*44704f69SBart Van Assche             return 1;
1194*44704f69SBart Van Assche         }
1195*44704f69SBart Van Assche         cp = np;
1196*44704f69SBart Van Assche     } while (cp);
1197*44704f69SBart Van Assche     return 0;
1198*44704f69SBart Van Assche }
1199*44704f69SBart Van Assche 
1200*44704f69SBart Van Assche /* Returns open input file descriptor (>= 0) or a negative value
1201*44704f69SBart Van Assche  * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
1202*44704f69SBart Van Assche  */
1203*44704f69SBart Van Assche static int
open_if(struct xcopy_fp_t * ifp,int vb)1204*44704f69SBart Van Assche open_if(struct xcopy_fp_t * ifp, int vb)
1205*44704f69SBart Van Assche {
1206*44704f69SBart Van Assche     int infd = -1, flags, fl, res, err;
1207*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
1208*44704f69SBart Van Assche 
1209*44704f69SBart Van Assche     ifp->sg_type = dd_filetype(ifp);
1210*44704f69SBart Van Assche 
1211*44704f69SBart Van Assche     if (vb)
1212*44704f69SBart Van Assche         pr2serr(" >> Input file type: %s, devno %d:%d\n",
1213*44704f69SBart Van Assche                 dd_filetype_str(ifp->sg_type, ebuff),
1214*44704f69SBart Van Assche                 major(ifp->devno), minor(ifp->devno));
1215*44704f69SBart Van Assche     if (FT_ERROR & ifp->sg_type) {
1216*44704f69SBart Van Assche         pr2serr(ME "unable access %s\n", ifp->fname);
1217*44704f69SBart Van Assche         return -SG_LIB_FILE_ERROR;
1218*44704f69SBart Van Assche     }
1219*44704f69SBart Van Assche     flags = O_NONBLOCK;
1220*44704f69SBart Van Assche     if (ifp->excl)
1221*44704f69SBart Van Assche         flags |= O_EXCL;
1222*44704f69SBart Van Assche     fl = O_RDWR;
1223*44704f69SBart Van Assche     if ((infd = open(ifp->fname, fl | flags)) < 0) {
1224*44704f69SBart Van Assche         fl = O_RDONLY;
1225*44704f69SBart Van Assche         if ((infd = open(ifp->fname, fl | flags)) < 0) {
1226*44704f69SBart Van Assche             err = errno;
1227*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ,
1228*44704f69SBart Van Assche                      ME "could not open %.500s for sg reading", ifp->fname);
1229*44704f69SBart Van Assche             perror(ebuff);
1230*44704f69SBart Van Assche             return -sg_convert_errno(err);
1231*44704f69SBart Van Assche         }
1232*44704f69SBart Van Assche     }
1233*44704f69SBart Van Assche     if (vb)
1234*44704f69SBart Van Assche         pr2serr("        open input(sg_io), flags=0x%x\n", fl | flags);
1235*44704f69SBart Van Assche 
1236*44704f69SBart Van Assche     if (ifp->flock) {
1237*44704f69SBart Van Assche         res = flock(infd, LOCK_EX | LOCK_NB);
1238*44704f69SBart Van Assche         if (res < 0) {
1239*44704f69SBart Van Assche             close(infd);
1240*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %.500s "
1241*44704f69SBart Van Assche                      "failed", ifp->fname);
1242*44704f69SBart Van Assche             perror(ebuff);
1243*44704f69SBart Van Assche             return -SG_LIB_FLOCK_ERR;
1244*44704f69SBart Van Assche         }
1245*44704f69SBart Van Assche     }
1246*44704f69SBart Van Assche     return infd;
1247*44704f69SBart Van Assche }
1248*44704f69SBart Van Assche 
1249*44704f69SBart Van Assche /* Returns open output file descriptor (>= 0), -1 for don't
1250*44704f69SBart Van Assche  * bother opening (e.g. /dev/null), or a more negative value
1251*44704f69SBart Van Assche  * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
1252*44704f69SBart Van Assche  */
1253*44704f69SBart Van Assche static int
open_of(struct xcopy_fp_t * ofp,int vb)1254*44704f69SBart Van Assche open_of(struct xcopy_fp_t * ofp, int vb)
1255*44704f69SBart Van Assche {
1256*44704f69SBart Van Assche     int outfd, flags, res, err;
1257*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
1258*44704f69SBart Van Assche 
1259*44704f69SBart Van Assche     ofp->sg_type = dd_filetype(ofp);
1260*44704f69SBart Van Assche     if (vb)
1261*44704f69SBart Van Assche         pr2serr(" >> Output file type: %s, devno %d:%d\n",
1262*44704f69SBart Van Assche                 dd_filetype_str(ofp->sg_type, ebuff),
1263*44704f69SBart Van Assche                 major(ofp->devno), minor(ofp->devno));
1264*44704f69SBart Van Assche 
1265*44704f69SBart Van Assche     if (!(FT_DEV_NULL & ofp->sg_type)) {
1266*44704f69SBart Van Assche         flags = O_RDWR | O_NONBLOCK;
1267*44704f69SBart Van Assche         if (ofp->excl)
1268*44704f69SBart Van Assche             flags |= O_EXCL;
1269*44704f69SBart Van Assche         if (ofp->append)
1270*44704f69SBart Van Assche             flags |= O_APPEND;
1271*44704f69SBart Van Assche         if ((outfd = open(ofp->fname, flags)) < 0) {
1272*44704f69SBart Van Assche             err = errno;
1273*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ,
1274*44704f69SBart Van Assche                      ME "could not open %.500s for sg writing", ofp->fname);
1275*44704f69SBart Van Assche             perror(ebuff);
1276*44704f69SBart Van Assche             return -sg_convert_errno(err);
1277*44704f69SBart Van Assche         }
1278*44704f69SBart Van Assche         if (vb)
1279*44704f69SBart Van Assche             pr2serr("        open output(sg_io), flags=0x%x\n", flags);
1280*44704f69SBart Van Assche     } else
1281*44704f69SBart Van Assche         outfd = -1; /* don't bother opening */
1282*44704f69SBart Van Assche     if ((outfd >= 0) && ofp->flock) {
1283*44704f69SBart Van Assche         res = flock(outfd, LOCK_EX | LOCK_NB);
1284*44704f69SBart Van Assche         if (res < 0) {
1285*44704f69SBart Van Assche             close(outfd);
1286*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %.500s "
1287*44704f69SBart Van Assche                      "failed", ofp->fname);
1288*44704f69SBart Van Assche             perror(ebuff);
1289*44704f69SBart Van Assche             return -SG_LIB_FLOCK_ERR;
1290*44704f69SBart Van Assche         }
1291*44704f69SBart Van Assche     }
1292*44704f69SBart Van Assche     return outfd;
1293*44704f69SBart Van Assche }
1294*44704f69SBart Van Assche 
1295*44704f69SBart Van Assche static int
num_chs_in_str(const char * s,int slen,int ch)1296*44704f69SBart Van Assche num_chs_in_str(const char * s, int slen, int ch)
1297*44704f69SBart Van Assche {
1298*44704f69SBart Van Assche     int res = 0;
1299*44704f69SBart Van Assche 
1300*44704f69SBart Van Assche     while (--slen >= 0) {
1301*44704f69SBart Van Assche         if (ch == s[slen])
1302*44704f69SBart Van Assche             ++res;
1303*44704f69SBart Van Assche     }
1304*44704f69SBart Van Assche     return res;
1305*44704f69SBart Van Assche }
1306*44704f69SBart Van Assche 
1307*44704f69SBart Van Assche 
1308*44704f69SBart Van Assche int
main(int argc,char * argv[])1309*44704f69SBart Van Assche main(int argc, char * argv[])
1310*44704f69SBart Van Assche {
1311*44704f69SBart Van Assche     bool bpt_given = false;
1312*44704f69SBart Van Assche     bool list_id_given = false;
1313*44704f69SBart Van Assche     bool on_src = false;
1314*44704f69SBart Van Assche     bool on_src_dst_given = false;
1315*44704f69SBart Van Assche     bool verbose_given = false;
1316*44704f69SBart Van Assche     bool version_given = false;
1317*44704f69SBart Van Assche     int res, k, n, keylen, infd, outfd, xcopy_fd;
1318*44704f69SBart Van Assche     int blocks = 0;
1319*44704f69SBart Van Assche     int bpt = DEF_BLOCKS_PER_TRANSFER;
1320*44704f69SBart Van Assche     int dst_desc_len;
1321*44704f69SBart Van Assche     int ibs = 0;
1322*44704f69SBart Van Assche     int num_help = 0;
1323*44704f69SBart Van Assche     int num_xcopy = 0;
1324*44704f69SBart Van Assche     int obs = 0;
1325*44704f69SBart Van Assche     int ret = 0;
1326*44704f69SBart Van Assche     int seg_desc_type;
1327*44704f69SBart Van Assche     int src_desc_len;
1328*44704f69SBart Van Assche     int64_t skip = 0;
1329*44704f69SBart Van Assche     int64_t seek = 0;
1330*44704f69SBart Van Assche     uint8_t list_id = 1;
1331*44704f69SBart Van Assche     char * key;
1332*44704f69SBart Van Assche     char * buf;
1333*44704f69SBart Van Assche     char str[STR_SZ];
1334*44704f69SBart Van Assche     uint8_t src_desc[256];
1335*44704f69SBart Van Assche     uint8_t dst_desc[256];
1336*44704f69SBart Van Assche 
1337*44704f69SBart Van Assche     ixcf.fname[0] = '\0';
1338*44704f69SBart Van Assche     oxcf.fname[0] = '\0';
1339*44704f69SBart Van Assche     ixcf.num_sect = -1;
1340*44704f69SBart Van Assche     oxcf.num_sect = -1;
1341*44704f69SBart Van Assche 
1342*44704f69SBart Van Assche     if (argc < 2) {
1343*44704f69SBart Van Assche         pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n");
1344*44704f69SBart Van Assche         pr2serr("For more information use '--help'\n");
1345*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
1346*44704f69SBart Van Assche     }
1347*44704f69SBart Van Assche 
1348*44704f69SBart Van Assche     for (k = 1; k < argc; k++) {
1349*44704f69SBart Van Assche         if (argv[k]) {
1350*44704f69SBart Van Assche             strncpy(str, argv[k], STR_SZ - 1);
1351*44704f69SBart Van Assche             str[STR_SZ - 1] = '\0';
1352*44704f69SBart Van Assche         } else
1353*44704f69SBart Van Assche             continue;
1354*44704f69SBart Van Assche         for (key = str, buf = key; *buf && *buf != '=';)
1355*44704f69SBart Van Assche             buf++;
1356*44704f69SBart Van Assche         if (*buf)
1357*44704f69SBart Van Assche             *buf++ = '\0';
1358*44704f69SBart Van Assche         keylen = (int)strlen(key);
1359*44704f69SBart Van Assche         if (0 == strncmp(key, "app", 3)) {
1360*44704f69SBart Van Assche             ixcf.append = !! sg_get_num(buf);
1361*44704f69SBart Van Assche             oxcf.append = ixcf.append;
1362*44704f69SBart Van Assche         } else if (0 == strcmp(key, "bpt")) {
1363*44704f69SBart Van Assche             bpt = sg_get_num(buf);
1364*44704f69SBart Van Assche             if (-1 == bpt) {
1365*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'bpt='\n");
1366*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1367*44704f69SBart Van Assche             }
1368*44704f69SBart Van Assche             bpt_given = true;
1369*44704f69SBart Van Assche         } else if (0 == strcmp(key, "bs")) {
1370*44704f69SBart Van Assche             blk_sz = sg_get_num(buf);
1371*44704f69SBart Van Assche             if (-1 == blk_sz) {
1372*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'bs='\n");
1373*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1374*44704f69SBart Van Assche             }
1375*44704f69SBart Van Assche         } else if (0 == strcmp(key, "list_id")) {
1376*44704f69SBart Van Assche             ret = sg_get_num(buf);
1377*44704f69SBart Van Assche             if (-1 == ret || ret > 0xff) {
1378*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'list_id='\n");
1379*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1380*44704f69SBart Van Assche             }
1381*44704f69SBart Van Assche             list_id = (ret & 0xff);
1382*44704f69SBart Van Assche             list_id_given = true;
1383*44704f69SBart Van Assche         } else if (0 == strcmp(key, "id_usage")) {
1384*44704f69SBart Van Assche             if (!strncmp(buf, "hold", 4))
1385*44704f69SBart Van Assche                 list_id_usage = 0;
1386*44704f69SBart Van Assche             else if (!strncmp(buf, "discard", 7))
1387*44704f69SBart Van Assche                 list_id_usage = 2;
1388*44704f69SBart Van Assche             else if (!strncmp(buf, "disable", 7))
1389*44704f69SBart Van Assche                 list_id_usage = 3;
1390*44704f69SBart Van Assche             else {
1391*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'id_usage='\n");
1392*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1393*44704f69SBart Van Assche             }
1394*44704f69SBart Van Assche         } else if (0 == strcmp(key, "conv"))
1395*44704f69SBart Van Assche             pr2serr(ME ">>> ignoring all 'conv=' arguments\n");
1396*44704f69SBart Van Assche         else if (0 == strcmp(key, "count")) {
1397*44704f69SBart Van Assche             if (0 != strcmp("-1", buf)) {
1398*44704f69SBart Van Assche                 dd_count = sg_get_llnum(buf);
1399*44704f69SBart Van Assche                 if (-1LL == dd_count) {
1400*44704f69SBart Van Assche                     pr2serr(ME "bad argument to 'count='\n");
1401*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
1402*44704f69SBart Van Assche                 }
1403*44704f69SBart Van Assche             }   /* treat 'count=-1' as calculate count (same as not given) */
1404*44704f69SBart Van Assche         } else if (0 == strcmp(key, "prio")) {
1405*44704f69SBart Van Assche             priority = sg_get_num(buf);
1406*44704f69SBart Van Assche         } else if (0 == strcmp(key, "cat")) {
1407*44704f69SBart Van Assche             n = sg_get_num(buf);
1408*44704f69SBart Van Assche             if (n < 0 || n > 1) {
1409*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'cat='\n");
1410*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1411*44704f69SBart Van Assche             }
1412*44704f69SBart Van Assche             xcopy_flag_cat = !! n;
1413*44704f69SBart Van Assche         } else if (0 == strcmp(key, "dc")) {
1414*44704f69SBart Van Assche             n = sg_get_num(buf);
1415*44704f69SBart Van Assche             if (n < 0 || n > 1) {
1416*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'dc='\n");
1417*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1418*44704f69SBart Van Assche             }
1419*44704f69SBart Van Assche             xcopy_flag_dc = !! n;
1420*44704f69SBart Van Assche         } else if (0 == strcmp(key, "fco")) {
1421*44704f69SBart Van Assche             n = sg_get_num(buf);
1422*44704f69SBart Van Assche             if (n < 0 || n > 1) {
1423*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'fco='\n");
1424*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1425*44704f69SBart Van Assche             }
1426*44704f69SBart Van Assche             xcopy_flag_fco = !! n;
1427*44704f69SBart Van Assche         } else if (0 == strcmp(key, "ibs")) {
1428*44704f69SBart Van Assche             ibs = sg_get_num(buf);
1429*44704f69SBart Van Assche         } else if (strcmp(key, "if") == 0) {
1430*44704f69SBart Van Assche             if ('\0' != ixcf.fname[0]) {
1431*44704f69SBart Van Assche                 pr2serr("Second IFILE argument??\n");
1432*44704f69SBart Van Assche                 return SG_LIB_CONTRADICT;
1433*44704f69SBart Van Assche             } else {
1434*44704f69SBart Van Assche                 memcpy(ixcf.fname, buf, INOUTF_SZ - 1);
1435*44704f69SBart Van Assche                 ixcf.fname[INOUTF_SZ - 1] = '\0';
1436*44704f69SBart Van Assche             }
1437*44704f69SBart Van Assche         } else if (0 == strcmp(key, "iflag")) {
1438*44704f69SBart Van Assche             if (process_flags(buf, &ixcf)) {
1439*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'iflag='\n");
1440*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1441*44704f69SBart Van Assche             }
1442*44704f69SBart Van Assche         } else if (0 == strcmp(key, "obs")) {
1443*44704f69SBart Van Assche             obs = sg_get_num(buf);
1444*44704f69SBart Van Assche         } else if (strcmp(key, "of") == 0) {
1445*44704f69SBart Van Assche             if ('\0' != oxcf.fname[0]) {
1446*44704f69SBart Van Assche                 pr2serr("Second OFILE argument??\n");
1447*44704f69SBart Van Assche                 return SG_LIB_CONTRADICT;
1448*44704f69SBart Van Assche             } else {
1449*44704f69SBart Van Assche                 memcpy(oxcf.fname, buf, INOUTF_SZ - 1);
1450*44704f69SBart Van Assche                 oxcf.fname[INOUTF_SZ - 1] = '\0';
1451*44704f69SBart Van Assche             }
1452*44704f69SBart Van Assche         } else if (0 == strcmp(key, "oflag")) {
1453*44704f69SBart Van Assche             if (process_flags(buf, &oxcf)) {
1454*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'oflag='\n");
1455*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1456*44704f69SBart Van Assche             }
1457*44704f69SBart Van Assche         } else if (0 == strcmp(key, "seek")) {
1458*44704f69SBart Van Assche             seek = sg_get_llnum(buf);
1459*44704f69SBart Van Assche             if (-1LL == seek) {
1460*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'seek='\n");
1461*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1462*44704f69SBart Van Assche             }
1463*44704f69SBart Van Assche         } else if (0 == strcmp(key, "skip")) {
1464*44704f69SBart Van Assche             skip = sg_get_llnum(buf);
1465*44704f69SBart Van Assche             if (-1LL == skip) {
1466*44704f69SBart Van Assche                 pr2serr(ME "bad argument to 'skip='\n");
1467*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1468*44704f69SBart Van Assche             }
1469*44704f69SBart Van Assche         } else if (0 == strcmp(key, "time"))
1470*44704f69SBart Van Assche             do_time = !! sg_get_num(buf);
1471*44704f69SBart Van Assche         else if (0 == strncmp(key, "verb", 4))
1472*44704f69SBart Van Assche             verbose = sg_get_num(buf);
1473*44704f69SBart Van Assche         /* look for long options that start with '--' */
1474*44704f69SBart Van Assche         else if (0 == strncmp(key, "--help", 6))
1475*44704f69SBart Van Assche             ++num_help;
1476*44704f69SBart Van Assche         else if (0 == strncmp(key, "--on_dst", 8)) {
1477*44704f69SBart Van Assche             on_src = false;
1478*44704f69SBart Van Assche             if (on_src_dst_given) {
1479*44704f69SBart Van Assche                 pr2serr("Syntax error - either specify --on_src OR "
1480*44704f69SBart Van Assche                         "--on_dst\n");
1481*44704f69SBart Van Assche                 pr2serr("For more information use '--help'\n");
1482*44704f69SBart Van Assche                 return SG_LIB_CONTRADICT;
1483*44704f69SBart Van Assche             }
1484*44704f69SBart Van Assche             on_src_dst_given = true;
1485*44704f69SBart Van Assche         } else if (0 == strncmp(key, "--on_src", 8)) {
1486*44704f69SBart Van Assche             on_src = true;
1487*44704f69SBart Van Assche             if (on_src_dst_given) {
1488*44704f69SBart Van Assche                 pr2serr("Syntax error - either specify --on_src OR "
1489*44704f69SBart Van Assche                         "--on_dst\n");
1490*44704f69SBart Van Assche                 pr2serr("For more information use '--help'\n");
1491*44704f69SBart Van Assche                 return SG_LIB_CONTRADICT;
1492*44704f69SBart Van Assche             }
1493*44704f69SBart Van Assche             on_src_dst_given = true;
1494*44704f69SBart Van Assche         } else if (0 == strncmp(key, "--verb", 6)) {
1495*44704f69SBart Van Assche             verbose_given = true;
1496*44704f69SBart Van Assche             verbose += 1;
1497*44704f69SBart Van Assche         } else if (0 == strncmp(key, "--vers", 6))
1498*44704f69SBart Van Assche             version_given = true;
1499*44704f69SBart Van Assche         else if (0 == strncmp(key, "--xcopy", 7))
1500*44704f69SBart Van Assche             ;   /* ignore; for compatibility with ddpt */
1501*44704f69SBart Van Assche         /* look for short options that start with a single '-', they can be
1502*44704f69SBart Van Assche          * concaternated (e.g. '-vvvV') */
1503*44704f69SBart Van Assche         else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
1504*44704f69SBart Van Assche             res = 0;
1505*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'h');
1506*44704f69SBart Van Assche             num_help += n;
1507*44704f69SBart Van Assche             res += n;
1508*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'v');
1509*44704f69SBart Van Assche             verbose += n;
1510*44704f69SBart Van Assche             if (n > 0)
1511*44704f69SBart Van Assche                 verbose_given = true;
1512*44704f69SBart Van Assche             res += n;
1513*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'V');
1514*44704f69SBart Van Assche             if (n > 0)
1515*44704f69SBart Van Assche                 version_given = true;
1516*44704f69SBart Van Assche             res += n;
1517*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'x');
1518*44704f69SBart Van Assche             /* accept and ignore; for compatibility with ddpt */
1519*44704f69SBart Van Assche             res += n;
1520*44704f69SBart Van Assche             if (res < (keylen - 1)) {
1521*44704f69SBart Van Assche                 pr2serr(ME "Unrecognised short option in '%s', try "
1522*44704f69SBart Van Assche                         "'--help'\n", key);
1523*44704f69SBart Van Assche                 if (0 == num_help)
1524*44704f69SBart Van Assche                     return -1;
1525*44704f69SBart Van Assche             }
1526*44704f69SBart Van Assche         } else {
1527*44704f69SBart Van Assche             pr2serr("Unrecognized option '%s'\n", key);
1528*44704f69SBart Van Assche             if (num_help)
1529*44704f69SBart Van Assche                 usage(num_help);
1530*44704f69SBart Van Assche             else
1531*44704f69SBart Van Assche                 pr2serr("For more information use '--help'\n");
1532*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
1533*44704f69SBart Van Assche         }
1534*44704f69SBart Van Assche     }
1535*44704f69SBart Van Assche     if (num_help) {
1536*44704f69SBart Van Assche         usage(num_help);
1537*44704f69SBart Van Assche         return 0;
1538*44704f69SBart Van Assche     }
1539*44704f69SBart Van Assche #ifdef DEBUG
1540*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
1541*44704f69SBart Van Assche     if (verbose_given && version_given) {
1542*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1543*44704f69SBart Van Assche         verbose_given = false;
1544*44704f69SBart Van Assche         version_given = false;
1545*44704f69SBart Van Assche         verbose = 0;
1546*44704f69SBart Van Assche     } else if (! verbose_given) {
1547*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
1548*44704f69SBart Van Assche         verbose = 2;
1549*44704f69SBart Van Assche     } else
1550*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", verbose);
1551*44704f69SBart Van Assche #else
1552*44704f69SBart Van Assche     if (verbose_given && version_given)
1553*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1554*44704f69SBart Van Assche #endif
1555*44704f69SBart Van Assche     if (version_given) {
1556*44704f69SBart Van Assche         pr2serr(ME "%s\n", version_str);
1557*44704f69SBart Van Assche         return 0;
1558*44704f69SBart Van Assche     }
1559*44704f69SBart Van Assche 
1560*44704f69SBart Van Assche     if (! on_src_dst_given) {
1561*44704f69SBart Van Assche         if (ixcf.xcopy_given == oxcf.xcopy_given) {
1562*44704f69SBart Van Assche             char * csp;
1563*44704f69SBart Van Assche             char * cdp;
1564*44704f69SBart Van Assche 
1565*44704f69SBart Van Assche             csp = getenv(XCOPY_TO_SRC);
1566*44704f69SBart Van Assche             cdp = getenv(XCOPY_TO_DST);
1567*44704f69SBart Van Assche             if ((!! csp) == (!! cdp)) {
1568*44704f69SBart Van Assche #if DEF_XCOPY_SRC0_DST1 == 0
1569*44704f69SBart Van Assche                 on_src = true;
1570*44704f69SBart Van Assche #else
1571*44704f69SBart Van Assche                 on_src = false;
1572*44704f69SBart Van Assche #endif
1573*44704f69SBart Van Assche             } else if (csp)
1574*44704f69SBart Van Assche                 on_src = true;
1575*44704f69SBart Van Assche             else
1576*44704f69SBart Van Assche                 on_src = false;
1577*44704f69SBart Van Assche         } else if (ixcf.xcopy_given)
1578*44704f69SBart Van Assche             on_src = true;
1579*44704f69SBart Van Assche         else
1580*44704f69SBart Van Assche             on_src = false;
1581*44704f69SBart Van Assche     }
1582*44704f69SBart Van Assche     if (verbose > 1)
1583*44704f69SBart Van Assche         pr2serr(" >>> Extended Copy(LID1) command will be sent to %s device "
1584*44704f69SBart Van Assche                 "[%s]\n", (on_src ? "src" : "dst"),
1585*44704f69SBart Van Assche                 (on_src ? ixcf.fname : oxcf.fname));
1586*44704f69SBart Van Assche 
1587*44704f69SBart Van Assche     if ((ibs && blk_sz && (ibs != blk_sz)) ||
1588*44704f69SBart Van Assche         (obs && blk_sz && (obs != blk_sz))) {
1589*44704f69SBart Van Assche         pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
1590*44704f69SBart Van Assche         pr2serr("For more information use '--help'\n");
1591*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
1592*44704f69SBart Van Assche     }
1593*44704f69SBart Van Assche     if (blk_sz && !ibs)
1594*44704f69SBart Van Assche         ibs = blk_sz;
1595*44704f69SBart Van Assche     if (blk_sz && !obs)
1596*44704f69SBart Van Assche         obs = blk_sz;
1597*44704f69SBart Van Assche 
1598*44704f69SBart Van Assche     if ((skip < 0) || (seek < 0)) {
1599*44704f69SBart Van Assche         pr2serr("skip and seek cannot be negative\n");
1600*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
1601*44704f69SBart Van Assche     }
1602*44704f69SBart Van Assche     if (oxcf.append && (seek > 0)) {
1603*44704f69SBart Van Assche         pr2serr("Can't use both append and seek switches\n");
1604*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
1605*44704f69SBart Van Assche     }
1606*44704f69SBart Van Assche     if (bpt < 1) {
1607*44704f69SBart Van Assche         pr2serr("bpt must be greater than 0\n");
1608*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
1609*44704f69SBart Van Assche     } else if (bpt > MAX_BLOCKS_PER_TRANSFER) {
1610*44704f69SBart Van Assche         pr2serr("bpt must be less than or equal to %d\n",
1611*44704f69SBart Van Assche                 MAX_BLOCKS_PER_TRANSFER);
1612*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
1613*44704f69SBart Van Assche     }
1614*44704f69SBart Van Assche     if (list_id_usage == 3) { /* list_id usage disabled */
1615*44704f69SBart Van Assche         if (! list_id_given)
1616*44704f69SBart Van Assche             list_id = 0;
1617*44704f69SBart Van Assche         if (list_id) {
1618*44704f69SBart Van Assche             pr2serr("list_id disabled by id_usage flag\n");
1619*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
1620*44704f69SBart Van Assche         }
1621*44704f69SBart Van Assche     }
1622*44704f69SBart Van Assche 
1623*44704f69SBart Van Assche     if (verbose > 1)
1624*44704f69SBart Van Assche         pr2serr(" >>> " ME " if=%s skip=%" PRId64 " of=%s seek=%" PRId64
1625*44704f69SBart Van Assche                 " count=%" PRId64 "\n", ixcf.fname, skip, oxcf.fname, seek,
1626*44704f69SBart Van Assche                 dd_count);
1627*44704f69SBart Van Assche     install_handler(SIGINT, interrupt_handler);
1628*44704f69SBart Van Assche     install_handler(SIGQUIT, interrupt_handler);
1629*44704f69SBart Van Assche     install_handler(SIGPIPE, interrupt_handler);
1630*44704f69SBart Van Assche     install_handler(SIGUSR1, siginfo_handler);
1631*44704f69SBart Van Assche 
1632*44704f69SBart Van Assche     ixcf.pdt = -1;
1633*44704f69SBart Van Assche     oxcf.pdt = -1;
1634*44704f69SBart Van Assche     if (ixcf.fname[0] && ('-' != ixcf.fname[0])) {
1635*44704f69SBart Van Assche         infd = open_if(&ixcf, verbose);
1636*44704f69SBart Van Assche         if (infd < 0)
1637*44704f69SBart Van Assche             return -infd;
1638*44704f69SBart Van Assche     } else {
1639*44704f69SBart Van Assche         pr2serr("stdin not acceptable for IFILE\n");
1640*44704f69SBart Van Assche         return SG_LIB_FILE_ERROR;
1641*44704f69SBart Van Assche     }
1642*44704f69SBart Van Assche 
1643*44704f69SBart Van Assche     if (oxcf.fname[0] && ('-' != oxcf.fname[0])) {
1644*44704f69SBart Van Assche         outfd = open_of(&oxcf, verbose);
1645*44704f69SBart Van Assche         if (outfd < -1)
1646*44704f69SBart Van Assche             return -outfd;
1647*44704f69SBart Van Assche     } else {
1648*44704f69SBart Van Assche         pr2serr("stdout not acceptable for OFILE\n");
1649*44704f69SBart Van Assche         return SG_LIB_FILE_ERROR;
1650*44704f69SBart Van Assche     }
1651*44704f69SBart Van Assche 
1652*44704f69SBart Van Assche     res = open_sg(&ixcf, verbose);
1653*44704f69SBart Van Assche     if (res < 0) {
1654*44704f69SBart Van Assche         if (-1 == res)
1655*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
1656*44704f69SBart Van Assche         else
1657*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
1658*44704f69SBart Van Assche     }
1659*44704f69SBart Van Assche     res = open_sg(&oxcf, verbose);
1660*44704f69SBart Van Assche     if (res < 0) {
1661*44704f69SBart Van Assche         if (-1 == res)
1662*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
1663*44704f69SBart Van Assche         else
1664*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
1665*44704f69SBart Van Assche     }
1666*44704f69SBart Van Assche 
1667*44704f69SBart Van Assche     if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
1668*44704f69SBart Van Assche         pr2serr("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
1669*44704f69SBart Van Assche         pr2serr("For more information use '--help'\n");
1670*44704f69SBart Van Assche         return SG_LIB_CONTRADICT;
1671*44704f69SBart Van Assche     }
1672*44704f69SBart Van Assche 
1673*44704f69SBart Van Assche     res = scsi_read_capacity(&ixcf);
1674*44704f69SBart Van Assche     if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1675*44704f69SBart Van Assche         pr2serr("Unit attention (%s in), continuing\n", read_cap_str);
1676*44704f69SBart Van Assche         res = scsi_read_capacity(&ixcf);
1677*44704f69SBart Van Assche     } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
1678*44704f69SBart Van Assche         pr2serr("Aborted command (%s in), continuing\n", read_cap_str);
1679*44704f69SBart Van Assche         res = scsi_read_capacity(&ixcf);
1680*44704f69SBart Van Assche     }
1681*44704f69SBart Van Assche     if (0 != res) {
1682*44704f69SBart Van Assche         if (res == SG_LIB_CAT_INVALID_OP)
1683*44704f69SBart Van Assche             pr2serr("%s command not supported on %s\n", read_cap_str,
1684*44704f69SBart Van Assche                     ixcf.fname);
1685*44704f69SBart Van Assche         else if (res == SG_LIB_CAT_NOT_READY)
1686*44704f69SBart Van Assche             pr2serr("%s failed on %s - not ready\n", read_cap_str,
1687*44704f69SBart Van Assche                     ixcf.fname);
1688*44704f69SBart Van Assche         else
1689*44704f69SBart Van Assche             pr2serr("Unable to %s on %s\n", read_cap_str, ixcf.fname);
1690*44704f69SBart Van Assche         ixcf.num_sect = -1;
1691*44704f69SBart Van Assche     } else if (ibs && ixcf.sect_sz != ibs) {
1692*44704f69SBart Van Assche         pr2serr(">> warning: block size on %s confusion: "
1693*44704f69SBart Van Assche                 "ibs=%d, device claims=%d\n", ixcf.fname, ibs, ixcf.sect_sz);
1694*44704f69SBart Van Assche     }
1695*44704f69SBart Van Assche     if (skip && ixcf.num_sect < skip) {
1696*44704f69SBart Van Assche         pr2serr("argument to 'skip=' exceeds device size (max %" PRId64 ")\n",
1697*44704f69SBart Van Assche                 ixcf.num_sect);
1698*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
1699*44704f69SBart Van Assche     }
1700*44704f69SBart Van Assche 
1701*44704f69SBart Van Assche     res = scsi_read_capacity(&oxcf);
1702*44704f69SBart Van Assche     if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1703*44704f69SBart Van Assche         pr2serr("Unit attention (%s out), continuing\n", read_cap_str);
1704*44704f69SBart Van Assche         res = scsi_read_capacity(&oxcf);
1705*44704f69SBart Van Assche     } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
1706*44704f69SBart Van Assche         pr2serr("Aborted command (%s out), continuing\n", read_cap_str);
1707*44704f69SBart Van Assche         res = scsi_read_capacity(&oxcf);
1708*44704f69SBart Van Assche     }
1709*44704f69SBart Van Assche     if (0 != res) {
1710*44704f69SBart Van Assche         if (res == SG_LIB_CAT_INVALID_OP)
1711*44704f69SBart Van Assche             pr2serr("%s command not supported on %s\n", read_cap_str,
1712*44704f69SBart Van Assche                     oxcf.fname);
1713*44704f69SBart Van Assche         else
1714*44704f69SBart Van Assche             pr2serr("Unable to %s on %s\n", read_cap_str, oxcf.fname);
1715*44704f69SBart Van Assche         oxcf.num_sect = -1;
1716*44704f69SBart Van Assche     } else if (obs && obs != oxcf.sect_sz) {
1717*44704f69SBart Van Assche         pr2serr(">> warning: block size on %s confusion: obs=%d, device "
1718*44704f69SBart Van Assche                 "claims=%d\n", oxcf.fname, obs, oxcf.sect_sz);
1719*44704f69SBart Van Assche     }
1720*44704f69SBart Van Assche     if (seek && oxcf.num_sect < seek) {
1721*44704f69SBart Van Assche         pr2serr("argument to 'seek=' exceeds device size (max %" PRId64 ")\n",
1722*44704f69SBart Van Assche                 oxcf.num_sect);
1723*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
1724*44704f69SBart Van Assche     }
1725*44704f69SBart Van Assche     if ((dd_count < 0) || ((verbose > 0) && (0 == dd_count))) {
1726*44704f69SBart Van Assche         if (xcopy_flag_dc == 0) {
1727*44704f69SBart Van Assche             dd_count = ixcf.num_sect - skip;
1728*44704f69SBart Van Assche             if ((dd_count * ixcf.sect_sz) >
1729*44704f69SBart Van Assche                 ((oxcf.num_sect - seek) * oxcf.sect_sz))
1730*44704f69SBart Van Assche                 dd_count = (oxcf.num_sect - seek) * oxcf.sect_sz /
1731*44704f69SBart Van Assche                            ixcf.sect_sz;
1732*44704f69SBart Van Assche         } else {
1733*44704f69SBart Van Assche             dd_count = oxcf.num_sect - seek;
1734*44704f69SBart Van Assche             if ((dd_count * oxcf.sect_sz) >
1735*44704f69SBart Van Assche                 ((ixcf.num_sect - skip) * ixcf.sect_sz))
1736*44704f69SBart Van Assche                 dd_count = (ixcf.num_sect - skip) * ixcf.sect_sz /
1737*44704f69SBart Van Assche                            oxcf.sect_sz;
1738*44704f69SBart Van Assche         }
1739*44704f69SBart Van Assche     } else {
1740*44704f69SBart Van Assche         int64_t dd_bytes;
1741*44704f69SBart Van Assche 
1742*44704f69SBart Van Assche         if (xcopy_flag_dc)
1743*44704f69SBart Van Assche             dd_bytes = dd_count * oxcf.sect_sz;
1744*44704f69SBart Van Assche         else
1745*44704f69SBart Van Assche             dd_bytes = dd_count * ixcf.sect_sz;
1746*44704f69SBart Van Assche 
1747*44704f69SBart Van Assche         if (dd_bytes > ixcf.num_sect * ixcf.sect_sz) {
1748*44704f69SBart Van Assche             pr2serr("access beyond end of source device (max %" PRId64 ")\n",
1749*44704f69SBart Van Assche                     ixcf.num_sect);
1750*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
1751*44704f69SBart Van Assche         }
1752*44704f69SBart Van Assche         if (dd_bytes > oxcf.num_sect * oxcf.sect_sz) {
1753*44704f69SBart Van Assche             pr2serr("access beyond end of target device (max %" PRId64 ")\n",
1754*44704f69SBart Van Assche                     oxcf.num_sect);
1755*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
1756*44704f69SBart Van Assche         }
1757*44704f69SBart Van Assche     }
1758*44704f69SBart Van Assche 
1759*44704f69SBart Van Assche     res = scsi_operating_parameter(&ixcf, 0);
1760*44704f69SBart Van Assche     if (res < 0) {
1761*44704f69SBart Van Assche         if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
1762*44704f69SBart Van Assche             pr2serr("Unit attention (%s), continuing\n",
1763*44704f69SBart Van Assche                     rec_copy_op_params_str);
1764*44704f69SBart Van Assche             res = scsi_operating_parameter(&ixcf, 0);
1765*44704f69SBart Van Assche         }
1766*44704f69SBart Van Assche         if (-res == SG_LIB_CAT_INVALID_OP) {
1767*44704f69SBart Van Assche             pr2serr("%s command not supported on %s\n",
1768*44704f69SBart Van Assche                     rec_copy_op_params_str, ixcf.fname);
1769*44704f69SBart Van Assche             ret = sg_convert_errno(EINVAL);
1770*44704f69SBart Van Assche             goto fini;
1771*44704f69SBart Van Assche         } else if (-res == SG_LIB_CAT_NOT_READY)
1772*44704f69SBart Van Assche             pr2serr("%s failed on %s - not ready\n",
1773*44704f69SBart Van Assche                     rec_copy_op_params_str, ixcf.fname);
1774*44704f69SBart Van Assche         else {
1775*44704f69SBart Van Assche             pr2serr("Unable to %s on %s\n", rec_copy_op_params_str,
1776*44704f69SBart Van Assche                     ixcf.fname);
1777*44704f69SBart Van Assche             ret = -res;
1778*44704f69SBart Van Assche             goto fini;
1779*44704f69SBart Van Assche         }
1780*44704f69SBart Van Assche     } else if (res == 0) {
1781*44704f69SBart Van Assche         ret = SG_LIB_CAT_INVALID_OP;
1782*44704f69SBart Van Assche         goto fini;
1783*44704f69SBart Van Assche     }
1784*44704f69SBart Van Assche 
1785*44704f69SBart Van Assche     if (res & TD_VPD) {
1786*44704f69SBart Van Assche         if (verbose)
1787*44704f69SBart Van Assche             pr2serr("  >> using VPD identification for source %s\n",
1788*44704f69SBart Van Assche                     ixcf.fname);
1789*44704f69SBart Van Assche         src_desc_len = desc_from_vpd_id(ixcf.sg_fd, src_desc,
1790*44704f69SBart Van Assche                                  sizeof(src_desc), ixcf.sect_sz, ixcf.pad);
1791*44704f69SBart Van Assche         if (src_desc_len > (int)sizeof(src_desc)) {
1792*44704f69SBart Van Assche             pr2serr("source descriptor too large (%d bytes)\n", res);
1793*44704f69SBart Van Assche             ret = SG_LIB_CAT_MALFORMED;
1794*44704f69SBart Van Assche             goto fini;
1795*44704f69SBart Van Assche         }
1796*44704f69SBart Van Assche     } else {
1797*44704f69SBart Van Assche         ret = SG_LIB_CAT_INVALID_OP;
1798*44704f69SBart Van Assche         goto fini;
1799*44704f69SBart Van Assche     }
1800*44704f69SBart Van Assche 
1801*44704f69SBart Van Assche     res = scsi_operating_parameter(&oxcf, 1);
1802*44704f69SBart Van Assche     if (res < 0) {
1803*44704f69SBart Van Assche         if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
1804*44704f69SBart Van Assche             pr2serr("Unit attention (%s), continuing\n",
1805*44704f69SBart Van Assche                     rec_copy_op_params_str);
1806*44704f69SBart Van Assche             res = scsi_operating_parameter(&oxcf, 1);
1807*44704f69SBart Van Assche         }
1808*44704f69SBart Van Assche         if (-res == SG_LIB_CAT_INVALID_OP) {
1809*44704f69SBart Van Assche             pr2serr("%s command not supported on %s\n",
1810*44704f69SBart Van Assche                     rec_copy_op_params_str, oxcf.fname);
1811*44704f69SBart Van Assche             ret = sg_convert_errno(EINVAL);
1812*44704f69SBart Van Assche             goto fini;
1813*44704f69SBart Van Assche         } else if (-res == SG_LIB_CAT_NOT_READY)
1814*44704f69SBart Van Assche             pr2serr("%s failed on %s - not ready\n",
1815*44704f69SBart Van Assche                     rec_copy_op_params_str, oxcf.fname);
1816*44704f69SBart Van Assche         else {
1817*44704f69SBart Van Assche             pr2serr("Unable to %s on %s\n", rec_copy_op_params_str,
1818*44704f69SBart Van Assche                     oxcf.fname);
1819*44704f69SBart Van Assche             ret = -res;
1820*44704f69SBart Van Assche             goto fini;
1821*44704f69SBart Van Assche         }
1822*44704f69SBart Van Assche     } else if (res == 0) {
1823*44704f69SBart Van Assche         ret = SG_LIB_CAT_INVALID_OP;
1824*44704f69SBart Van Assche         goto fini;
1825*44704f69SBart Van Assche     }
1826*44704f69SBart Van Assche 
1827*44704f69SBart Van Assche     if (res & TD_VPD) {
1828*44704f69SBart Van Assche         if (verbose)
1829*44704f69SBart Van Assche             pr2serr("  >> using VPD identification for destination %s\n",
1830*44704f69SBart Van Assche                     oxcf.fname);
1831*44704f69SBart Van Assche         dst_desc_len = desc_from_vpd_id(oxcf.sg_fd, dst_desc,
1832*44704f69SBart Van Assche                                  sizeof(dst_desc), oxcf.sect_sz, oxcf.pad);
1833*44704f69SBart Van Assche         if (dst_desc_len > (int)sizeof(dst_desc)) {
1834*44704f69SBart Van Assche             pr2serr("destination descriptor too large (%d bytes)\n", res);
1835*44704f69SBart Van Assche             ret = SG_LIB_CAT_MALFORMED;
1836*44704f69SBart Van Assche             goto fini;
1837*44704f69SBart Van Assche         }
1838*44704f69SBart Van Assche     } else {
1839*44704f69SBart Van Assche         ret = SG_LIB_CAT_INVALID_OP;
1840*44704f69SBart Van Assche         goto fini;
1841*44704f69SBart Van Assche     }
1842*44704f69SBart Van Assche 
1843*44704f69SBart Van Assche     if (dd_count < 0) {
1844*44704f69SBart Van Assche         pr2serr("Couldn't calculate count, please give one\n");
1845*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
1846*44704f69SBart Van Assche     }
1847*44704f69SBart Van Assche 
1848*44704f69SBart Van Assche     if (dd_count < (ixcf.min_bytes / (uint32_t)ixcf.sect_sz)) {
1849*44704f69SBart Van Assche         pr2serr("not enough data to read (min %" PRIu32 " bytes)\n",
1850*44704f69SBart Van Assche                 oxcf.min_bytes);
1851*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
1852*44704f69SBart Van Assche     }
1853*44704f69SBart Van Assche     if (dd_count < (oxcf.min_bytes / (uint32_t)oxcf.sect_sz)) {
1854*44704f69SBart Van Assche         pr2serr("not enough data to write (min %" PRIu32 " bytes)\n",
1855*44704f69SBart Van Assche                 oxcf.min_bytes);
1856*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
1857*44704f69SBart Van Assche     }
1858*44704f69SBart Van Assche 
1859*44704f69SBart Van Assche     if (bpt_given) {
1860*44704f69SBart Van Assche         if (xcopy_flag_dc) {
1861*44704f69SBart Van Assche             if ((uint32_t)(bpt * oxcf.sect_sz) > oxcf.max_bytes) {
1862*44704f69SBart Van Assche                 pr2serr("bpt too large (max %" PRIu32 " blocks)\n",
1863*44704f69SBart Van Assche                         oxcf.max_bytes / (uint32_t)oxcf.sect_sz);
1864*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1865*44704f69SBart Van Assche             }
1866*44704f69SBart Van Assche         } else {
1867*44704f69SBart Van Assche             if ((uint32_t)(bpt * ixcf.sect_sz) > ixcf.max_bytes) {
1868*44704f69SBart Van Assche                 pr2serr("bpt too large (max %" PRIu32 " blocks)\n",
1869*44704f69SBart Van Assche                         ixcf.max_bytes / (uint32_t)ixcf.sect_sz);
1870*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
1871*44704f69SBart Van Assche             }
1872*44704f69SBart Van Assche         }
1873*44704f69SBart Van Assche     } else {
1874*44704f69SBart Van Assche         uint32_t r;
1875*44704f69SBart Van Assche 
1876*44704f69SBart Van Assche         if (xcopy_flag_dc)
1877*44704f69SBart Van Assche             r = oxcf.max_bytes / (uint32_t)oxcf.sect_sz;
1878*44704f69SBart Van Assche         else
1879*44704f69SBart Van Assche             r = ixcf.max_bytes / (uint32_t)ixcf.sect_sz;
1880*44704f69SBart Van Assche         bpt = (r > MAX_BLOCKS_PER_TRANSFER) ? MAX_BLOCKS_PER_TRANSFER : r;
1881*44704f69SBart Van Assche     }
1882*44704f69SBart Van Assche 
1883*44704f69SBart Van Assche     seg_desc_type = seg_desc_from_dd_type(simplified_ft(&ixcf), 0,
1884*44704f69SBart Van Assche                                           simplified_ft(&oxcf), 0);
1885*44704f69SBart Van Assche 
1886*44704f69SBart Van Assche     if (do_time) {
1887*44704f69SBart Van Assche         start_tm.tv_sec = 0;
1888*44704f69SBart Van Assche         start_tm.tv_usec = 0;
1889*44704f69SBart Van Assche         gettimeofday(&start_tm, NULL);
1890*44704f69SBart Van Assche         start_tm_valid = true;
1891*44704f69SBart Van Assche     }
1892*44704f69SBart Van Assche 
1893*44704f69SBart Van Assche     if (verbose)
1894*44704f69SBart Van Assche         pr2serr("Start of loop, count=%" PRId64 ", bpt=%d, lba_in=%" PRId64
1895*44704f69SBart Van Assche                 ", lba_out=%" PRId64 "\n", dd_count, bpt, skip, seek);
1896*44704f69SBart Van Assche 
1897*44704f69SBart Van Assche     xcopy_fd = (on_src) ? infd : outfd;
1898*44704f69SBart Van Assche 
1899*44704f69SBart Van Assche     while (dd_count > 0) {
1900*44704f69SBart Van Assche         if (dd_count > bpt)
1901*44704f69SBart Van Assche             blocks = bpt;
1902*44704f69SBart Van Assche         else
1903*44704f69SBart Van Assche             blocks = dd_count;
1904*44704f69SBart Van Assche         res = scsi_extended_copy(xcopy_fd, list_id, src_desc, src_desc_len,
1905*44704f69SBart Van Assche                                  dst_desc, dst_desc_len, seg_desc_type,
1906*44704f69SBart Van Assche                                  blocks, skip, seek);
1907*44704f69SBart Van Assche         if (res != 0)
1908*44704f69SBart Van Assche             break;
1909*44704f69SBart Van Assche         in_full += blocks;
1910*44704f69SBart Van Assche         skip += blocks;
1911*44704f69SBart Van Assche         seek += blocks;
1912*44704f69SBart Van Assche         dd_count -= blocks;
1913*44704f69SBart Van Assche         num_xcopy++;
1914*44704f69SBart Van Assche     }
1915*44704f69SBart Van Assche 
1916*44704f69SBart Van Assche     if (do_time)
1917*44704f69SBart Van Assche         calc_duration_throughput(0);
1918*44704f69SBart Van Assche     if (res)
1919*44704f69SBart Van Assche         pr2serr("sg_xcopy: failed with error %d (%" PRId64 " blocks left)\n",
1920*44704f69SBart Van Assche                 res, dd_count);
1921*44704f69SBart Van Assche     else
1922*44704f69SBart Van Assche         pr2serr("sg_xcopy: %" PRId64 " blocks, %d command%s\n", in_full,
1923*44704f69SBart Van Assche                 num_xcopy, ((num_xcopy > 1) ? "s" : ""));
1924*44704f69SBart Van Assche     ret = res;
1925*44704f69SBart Van Assche 
1926*44704f69SBart Van Assche fini:
1927*44704f69SBart Van Assche     /* file handles not explicitly closed; let process cleanup do that */
1928*44704f69SBart Van Assche     if (0 == verbose) {
1929*44704f69SBart Van Assche         if (! sg_if_can2stderr("sg_xcopy failed: ", ret))
1930*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
1931*44704f69SBart Van Assche                     "more information\n");
1932*44704f69SBart Van Assche     }
1933*44704f69SBart Van Assche     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1934*44704f69SBart Van Assche }
1935