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