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