1 /*
2  * Copyright (c) 2014 Douglas Gilbert.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /* ddpt is a utility program for copying files. It broadly follows the syntax
31  * and semantics of the "dd" program found in Unix. ddpt is specialised for
32  * "files" that represent storage devices, especially those that understand
33  * the SCSI command set accessed via a pass-through.
34  */
35 
36 /*
37  * This utility, ddptctl, is an auxiliary to do related tasks for ddpt.
38  * That way ddpt can concentrate on copy (or partial copy) operations.
39  */
40 
41 /* Need _GNU_SOURCE for O_DIRECT */
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44 #endif
45 
46 #include <unistd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <getopt.h>
52 #include <errno.h>
53 #define __STDC_LIMIT_MACROS 1   /* for UINT64_MAX, UINT32_MAX, etc */
54 #include <limits.h>
55 #include <fcntl.h>
56 #define __STDC_FORMAT_MACROS 1
57 #include <inttypes.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 
61 /* N.B. config.h must precede anything that depends on HAVE_*  */
62 #ifdef HAVE_CONFIG_H
63 #include "config.h"
64 #endif
65 
66 #include "ddpt.h"
67 
68 const char * ddptctl_version_str = "0.95 20141226 [svn: r307]";
69 
70 #ifdef SG_LIB_LINUX
71 #include <sys/ioctl.h>
72 #include <sys/sysmacros.h>
73 #include <sys/file.h>
74 #include <linux/major.h>
75 #include <linux/fs.h>   /* <sys/mount.h> */
76 #include <linux/mtio.h> /* For tape ioctls */
77 #ifndef MTWEOFI
78 #define MTWEOFI 35  /* write an end-of-file record (mark) in immediate mode */
79 #endif
80 
81 #ifdef HAVE_FALLOCATE
82 #include <linux/falloc.h>
83 #ifndef FALLOC_FL_KEEP_SIZE
84 #define FALLOC_FL_KEEP_SIZE     0x01    /* from lk 3.1 linux/falloc.h */
85 #endif
86 #endif
87 
88 #endif  /* SG_LIB_LINUX */
89 
90 #ifdef SG_LIB_FREEBSD
91 #include <sys/ioctl.h>
92 #include <libgen.h>
93 #ifndef __DragonFly__
94 #include <sys/disk.h>
95 #endif
96 #include <sys/filio.h>
97 #endif
98 
99 #ifdef SG_LIB_SOLARIS
100 #include <sys/ioctl.h>
101 #include <sys/dkio.h>
102 #endif
103 
104 #ifdef SG_LIB_WIN32
105 #ifndef SG_LIB_MINGW
106 /* cygwin */
107 #include <sys/ioctl.h>
108 #endif
109 #endif
110 
111 #include "sg_lib.h"
112 #include "sg_cmds_basic.h"
113 
114 
115 #define DEF_3PC_OUT_TIMEOUT (10 * 60)   /* 10 minutes not enough, use IMMED */
116 #define DEF_3PC_IN_TIMEOUT 60           /* these should be fast */
117 #define MAX_NUM_MAN_TOKS 32
118 
119 #define DEF_ROD_TOK_FILE "ddptctl_rod_tok.bin"
120 
121 
122 static struct scat_gath_elem fixed_sgl[MAX_FIXED_SGL_ELEMS];
123 
124 static struct option long_options[] = {
125         {"abort", no_argument, 0, 'A'},
126         {"all_toks", no_argument, 0, 'a'},
127         {"block", no_argument, 0, 'b'},
128         {"del_tkn", no_argument, 0, 'D'},
129         {"help", no_argument, 0, 'h'},
130         {"hex", no_argument, 0, 'H'},
131         {"info", no_argument, 0, 'i'},
132         {"immed", no_argument, 0, 'I'},
133         {"list_id", required_argument, 0, 'l'},
134         {"oir", required_argument, 0, 'O'},
135         {"poll", no_argument, 0, 'p'},
136         {"pt", required_argument, 0, 'P'},
137         {"readonly", no_argument, 0, 'y'},
138         {"receive", no_argument, 0, 'R'},
139         {"rtf", required_argument, 0, 'r'},
140         {"rtype", required_argument, 0, 't'},
141         {"timeout", required_argument, 0, 'T'},
142         {"size", no_argument, 0, 's'},
143         {"verbose", no_argument, 0, 'v'},
144         {"version", no_argument, 0, 'V'},
145         {"wut", required_argument, 0, 'w'},
146         {0, 0, 0, 0},
147 };
148 
149 
150 
151 static void
usage()152 usage()
153 {
154     pr2serr("Usage: "
155             "ddptctl [--abort] [--all_toks] [--block] [--del_tkn] [--help] "
156             "[--hex]\n"
157             "               [-immed] [--info] [--list_id=LID] [--oir=OIR] "
158             "[--poll]\n"
159             "               [--pt=GL] [--readonly] [--receive] [--rtf=RTF] "
160             "[rtype=RTYPE]\n"
161             "               [--size] [--timeout=ITO[,CMD]] [--verbose] "
162             "[--version]\n"
163             "               [--wut=SL] [DEVICE]\n"
164             "  where:\n"
165             "    --abort|-A            call COPY OPERATION ABORT command\n"
166             "    --all_toks|-a         call REPORT ALL ROD TOKENS command\n"
167             "    --block|-B            treat as block DEVICE (def: use "
168             "SCSI commands)\n"
169             "    --del_tkn|-D          set DEL_TKN bit in WUT command\n"
170             "    --help|-h             print out usage message\n"
171             "    --hex|-H              print response in ASCII hexadecimal\n"
172             "    --immed|-I            set IMMED bit in PT or WUT, exit "
173             "prior to\n"
174             "                          data transfer completion (then use "
175             "--poll)\n"
176             "    --info|-i             provide information on DEVICE or "
177             "RTF\n"
178             "    --list_id=LID|-l LID    LID is list identifier used with "
179             "PT, WUT,\n"
180             "                            RRTI or COPY OPERATION ABORT (def: "
181             "257)\n"
182             "    --oir=OIR|-O OIR      Offset In ROD (def: 0), used by WUT\n"
183             "    --poll|-p             call RRTI periodically until "
184             "completed\n"
185             "    --pt=GL|-P GL         call PT with gather list GL. GL's "
186             "format is\n"
187             "                          LBA1,NUM1[,LBA2,NUM2...]\n"
188             "    --readonly|-y         open DEVICE read-only (def: "
189             "read-write)\n"
190             "    --receive|-R          call RRTI once\n"
191             "    --rtf=RTF|-r RTF      ROD Token file for analysis (--info); "
192             "output by\n"
193             "                          -pt=, --poll or --receive; input to "
194             "--wut=\n"
195             "    --rtype=RTYPE|-t RTYPE    ROD type (def: RTV cleared in "
196             "PT command)\n"
197             "    --size|-s             get size of DEVICE (def: with SCSI "
198             "commands)\n"
199             "    --timeout=ITO[,CMD] | -T ITO[,CMD]\n"
200             "                          ITO is inactivity timeout (def: 0), "
201             "CMD is\n"
202             "                          command timeout (def: 600); units: "
203             "seconds\n"
204             "    --verbose|-v          increase verbosity\n"
205             "    --version|-V          print version string and exit\n"
206             "    --wut=SL|-w SL        call WUT with scatter list SL. SL's "
207             "format same\n"
208             "                          as GL\n\n"
209             "ddptctl is a ddpt helper utility, mainly for ODX, a subset of "
210             "xcopy(LID4).\nPT refers to the POPULATE TOKEN command, WUT to "
211             "the WRITE USING TOKEN\ncommand and RRTI to the RECEIVE ROD "
212             "TOKEN INFORMATION command. If\nthe ODX_RTF_LEN environment "
213             "variable is present, the ROD's size is\nappended to the ROD "
214             "Token placed in the RTF file.\n"
215             );
216 }
217 
218 /* If len==96 then assume it is a management ROD Token. Returns 0 if okay. */
219 static int
odx_print_rod_tok(const struct opts_t * op,unsigned char * rth,int len)220 odx_print_rod_tok(const struct opts_t * op, unsigned char * rth, int len)
221 {
222     int vendor, all_0, all_1, m, prot_en, p_type, desig_type, lbppbe;
223     int target_dev_desc = 0;
224     uint64_t bc;
225     uint32_t rod_t, bs;
226     uint16_t rtl;
227     char b[128];
228     unsigned char uc;
229 
230     rod_t = (rth[0] << 24) + (rth[1] << 16) + (rth[2] << 8) + rth[3];
231     printf("  ROD type: %s\n", rod_type_str(rod_t, b, sizeof(b)));
232     if (rod_t >= 0xfffffff0) {
233         printf("    Since ROD type is vendor specific, the following may "
234                "not be relevant\n");
235         vendor = 1;
236     } else {
237         vendor = 0;
238         target_dev_desc = (RODT_ACCESS_ON_REF == rod_t) ||
239                   (RODT_PIT_DEF == rod_t) || (RODT_PIT_VULN == rod_t) ||
240                   (RODT_PIT_PERS == rod_t);
241     }
242     rtl = (rth[6] << 8) + rth[7];
243     if (rtl < ODX_ROD_TOK_LEN_FLD) {
244         pr2serr(">>> ROD Token length field is too short, should be at "
245                 "least\n    %d bytes (0x%x), got 0x%" PRIx16 "\n",
246                 ODX_ROD_TOK_LEN_FLD, ODX_ROD_TOK_LEN_FLD, rtl);
247         if (! vendor)
248             return SG_LIB_CAT_OTHER;
249     }
250     printf("  Copy manager ROD Token identifier: %s\n",
251            rt_cm_id_str(rth, rtl + 8, b, sizeof(b)));
252     printf("  Creator Logical Unit descriptor:\n");
253     /* should make smaller version of following that outputs to stdout */
254     if (0xe4 != rth[16]) {
255         pr2serr(">>> Expected Identification descriptor CSCD (0xe4) got "
256                 "0x%x\n", rth[16]);
257         if (! vendor)
258             return SG_LIB_CAT_OTHER;
259     }
260     printf("    Peripheral Device type: 0x%x\n", rth[17] & 0x1f);
261     printf("    Relative initiator port identifier: 0x%x\n",
262            (rth[18] << 8) + rth[19]);
263     desig_type = rth[20 + 1] & 0xf;
264     if ((0x2 == desig_type) || (0x3 == desig_type))
265         decode_designation_descriptor(rth + 20, rth[23], 0, op->verbose);
266     else
267         printf("      Expected designator type of EUI-64 or NAA, got "
268                "0x%x\n", desig_type);
269 
270     /* A 16 byte integer worth of bytes! Seems like overkill. */
271     /* Look for all 0s or all 1s in the top 8 bytes */
272     all_0 = (0x0 == rth[48]);
273     if (all_0)
274         all_1 = 0;
275     else if (0xff == rth[48])
276         all_1 = 1;
277     else {
278         all_1 = 0;
279         printf("  Number of bytes represented: strange, bypass\n");
280         goto skip_to_bytes_rep;
281     }
282     for (m = 1; m < 8; m++) {
283         uc = rth[48 + m];
284         if (! (((0xff == uc) && all_1) || ((0 == uc) && all_0)))
285             break;
286     }
287     if (m < 8) {
288         printf("  Number of bytes represented: strange, bypass\n");
289         goto skip_to_bytes_rep;
290     }
291     bc = 0;
292     for (m = 0; m < 8; m++) {
293         if (m > 0)
294             bc <<= 8;
295         bc |= rth[56 + m];
296     }
297     if ((UINT64_MAX == bc) && all_1)
298         printf("  Number of bytes represented: unknown or too large\n");
299     else if (all_0)
300         printf("  Number of bytes represented: %" PRIu64 " [0x%" PRIx64
301                "]\n", bc, bc);
302     else
303         printf("  Number of bytes represented: strange (top 8 bytes "
304                "0xff)\n");
305 
306 skip_to_bytes_rep:
307     if (len <= 96)
308         return 0;
309     bs = ((rth[96] << 24) + (rth[97] << 16) + (rth[98] << 8) + rth[99]);
310     if (0 == bs) {
311         printf("  Device type specific data (for disk) has block size of "
312                "0; unlikely so skip\n");
313         goto skip_to_target_dev_desc;
314     }
315     printf("  Assume pdt=0 (e.g. disk) and decode device type specific "
316            "data:\n");
317     printf("    block size: %" PRIu32 " [0x%" PRIx32 "] bytes\n", bs, bs);
318     prot_en = !!(rth[100] & 0x1);
319     p_type = ((rth[100] >> 1) & 0x7);
320     printf("    Protection: prot_en=%d, p_type=%d, p_i_exponent=%d",
321            prot_en, p_type, ((rth[101] >> 4) & 0xf));
322     if (prot_en)
323         printf(" [type %d protection]\n", p_type + 1);
324     else
325         printf("\n");
326     printf("    Logical block provisioning: lbpme=%d, lbprz=%d\n",
327                    !!(rth[102] & 0x80), !!(rth[102] & 0x40));
328     lbppbe = rth[102] & 0xf;
329     printf("    Logical blocks per physical block exponent=%d\n", lbppbe);
330     if (lbppbe > 0)
331         printf("      [so physical block length=%" PRIu32 " bytes]\n",
332                bs * (1 << lbppbe));
333     printf("    Lowest aligned logical block address=%d\n",
334            ((rth[102] & 0x3f) << 8) + rth[103]);
335 
336 skip_to_target_dev_desc:
337     if (target_dev_desc) {
338         desig_type = rth[128 + 1] & 0xf;
339         if ((0x2 == desig_type) || (0x3 == desig_type) ||
340             (0x8 == desig_type) || op->verbose) {
341             printf("  Target device descriptor:\n");
342             decode_designation_descriptor(rth + 128, rth[131], 0,
343                                           op->verbose);
344         } else
345             printf("  Target device descriptor: unexpected designator "
346                    "type [0x%x]\n", desig_type);
347     }
348     return 0;
349 }
350 
351 static int
odx_rt_info(const struct opts_t * op)352 odx_rt_info(const struct opts_t * op)
353 {
354     int res, fd, err, k, m, bp_chunk, num;
355     int got_rtf_len = 0;
356     int a_err = 0;
357     uint64_t bc;
358     unsigned char rth[520];
359     struct stat st;
360 
361     if ('\0' == op->rtf[0]) {
362         pr2serr("odx_rt_info: expected ROD Token filename (rtf=RTF)\n");
363         return SG_LIB_FILE_ERROR;
364     }
365     if ((fd = open(op->rtf, O_RDONLY)) < 0) {
366         err = errno;
367         pr2serr("could not open '%s' for reading: %s\n", op->rtf,
368                 safe_strerror(err));
369         return SG_LIB_FILE_ERROR;
370     }
371     if (fstat(fd, &st) < 0) {
372         perror("fstat() on rtf");
373         return SG_LIB_FILE_ERROR;
374     }
375     res = st.st_size % 512;
376     if (res > 0) {
377         res = st.st_size % 520;
378         if (res > 0) {
379             pr2serr("rtf size is %d bytes, not a multiple of 512 or 520 "
380                     "bytes, so exit\n", (int)st.st_size);
381             return SG_LIB_FILE_ERROR;
382         }
383         ++got_rtf_len;
384     }
385     bp_chunk = got_rtf_len ? 520 : 512;
386     num = st.st_size / bp_chunk;
387     if (num > 1)
388         printf("Decoding file with %d ROD Tokens:\n", num);
389     for (k = 0; k < num; ++k) {
390         res = read(fd, rth, bp_chunk);
391         if (res < 0) {
392             err = errno;
393             pr2serr("could not read '%s': %s\n", op->rtf, safe_strerror(err));
394             close(fd);
395             return SG_LIB_FILE_ERROR;
396         }
397         if (res < bp_chunk) {
398             pr2serr("unable to read %d bytes from '%s', only got %d bytes\n",
399                      bp_chunk, op->rtf, res);
400             pr2serr("... it is unlikely file '%s' contains a ROD Token\n",
401                      op->rtf);
402             close(fd);
403             return SG_LIB_FILE_ERROR;
404         }
405         if (op->verbose > 3) {
406             pr2serr("Hex dump of chunk %d from rtf file:\n", bp_chunk);
407             dStrHexErr((const char *)rth, bp_chunk, 1);
408         }
409         if (num > 1)
410             printf("%s Decoding information from ROD Token %d\n",
411                    ((k > 0) ? "\n" : ""), k);
412         else
413             printf("Decoding information from ROD Token:\n");
414 
415         res = odx_print_rod_tok(op, rth, 512);
416         if (res && (0 == a_err))
417             a_err = res;
418 
419         if (got_rtf_len) {
420             for (m = 0, bc = 0; m < 8; ++m) {
421                 if (m > 0)
422                     bc <<= 8;
423                 bc += rth[512 + m];
424             }
425             printf("  Number of bytes represented: %" PRIu64 " [0x%" PRIx64
426                    "] (appended to token)\n", bc, bc);
427         }
428     }
429     close(fd);
430     return a_err;
431 }
432 
433 static int
do_copy_abort(struct opts_t * op)434 do_copy_abort(struct opts_t * op)
435 {
436     return pt_3party_copy_out(op->idip->fd, SA_COPY_ABORT, op->list_id,
437                               DEF_GROUP_NUM, DEF_3PC_OUT_TIMEOUT, NULL, 0, 1,
438                               op->verbose, op->verbose);
439 }
440 
441 
442 static int
report_all_toks(struct opts_t * op,struct dev_info_t * dip,int do_hex)443 report_all_toks(struct opts_t * op, struct dev_info_t * dip, int do_hex)
444 {
445     int res, fd, k;
446     int a_err = 0;
447     unsigned int len, num_mtoks;
448     unsigned char rsp[8 + (MAX_NUM_MAN_TOKS * 96)];
449     unsigned char * ucp;
450 
451     fd = dip->fd;
452     res = pt_3party_copy_in(fd, SA_ALL_ROD_TOKS, op->list_id,
453                             DEF_3PC_IN_TIMEOUT, rsp, sizeof(rsp), 1,
454                             op->verbose, op->verbose);
455     if (res)
456         return res;
457 
458     len = ((rsp[0] << 24) | (rsp[1] << 16) | (rsp[2] << 8) | rsp[3]) + 4;
459     if (len <= 8) {
460         printf("No management ROD Tokens reported\n");
461         if ((len < 8) && op->verbose)
462             pr2serr("  somewhat strange available_data=%u\n", len - 4);
463         return 0;
464     }
465     num_mtoks = (len - 8) / 96;
466     if ((0 != ((len - 8) % 96)) && op->verbose)
467         pr2serr("  available_data=%u implies non-integral number of "
468                 "management tokens\n", len - 4);
469     if (num_mtoks > MAX_NUM_MAN_TOKS) {
470         pr2serr("  %u management ROD Tokens available, can only display "
471                 "first %d\n", num_mtoks, MAX_NUM_MAN_TOKS);
472         num_mtoks = MAX_NUM_MAN_TOKS;
473     }
474     if (do_hex) {
475         if (do_hex > 1) {
476             dStrHex((const char *)rsp, ((len < 8) ? len : 8), 1);
477             for (k = 0, ucp = rsp + 8; k < (int)num_mtoks; ++k, ucp += 96) {
478                 printf("\n");
479                 dStrHex((const char *)ucp, 96, 1);
480             }
481         } else  {
482             if (len > sizeof(rsp))
483                 len = sizeof(rsp);
484             dStrHex((const char *)rsp, len , 1);
485         }
486         return 0;
487     }
488     printf("Number of managed ROD Token headers returned: %u\n",
489            (len - 8) / 96);
490     if (num_mtoks < ((len - 8) / 96))
491         printf("  Number displayed: %u\n", num_mtoks);
492     for (k = 0, ucp = rsp + 8; k < (int)num_mtoks; ++k, ucp += 96) {
493         printf("\n ROD Token header %d\n", k + 1);
494 
495         res = odx_print_rod_tok(op, ucp, 96);
496         if (res && (0 == a_err))
497             a_err = res;
498     }
499     return a_err;
500 }
501 
502 static int
write_to_rtf(struct opts_t * op,const struct rrti_resp_t * rp)503 write_to_rtf(struct opts_t * op, const struct rrti_resp_t * rp)
504 {
505     int res, len, err;
506     const char * cp;
507 
508     if (op->rtf_fd < 0) {
509         cp = DEF_ROD_TOK_FILE;
510         strncpy(op->rtf, cp, INOUTF_SZ - 1);
511         pr2serr("no --rtf=RTF given (or RTF broken) so writing ROD Token "
512                 "to %s\n", cp);
513         res = open_rtf(op);
514         if (res)
515             return SG_LIB_FILE_ERROR;
516     } else
517         cp = op->rtf;
518     len = (rp->rt_len > 512) ? 512 : rp->rt_len;
519     res = write(op->rtf_fd, rp->rod_tok, len);
520     if (res < 0) {
521         err = errno;
522         pr2serr("%s: unable to write to file: %s [%s]\n", __func__,
523                 cp, safe_strerror(err));
524         return SG_LIB_FILE_ERROR;
525     }
526     if (res < len) {
527         pr2serr("%s: short write to file: %s, wanted %d, got %d\n",
528                 __func__, cp, len, res);
529         return SG_LIB_CAT_OTHER;
530     }
531     return 0;
532 }
533 
534 /* Returns the number of times 'ch' is found in string 's' given the
535  * string's length. */
536 static int
num_chs_in_str(const char * s,int slen,int ch)537 num_chs_in_str(const char * s, int slen, int ch)
538 {
539     int res = 0;
540 
541     while (--slen >= 0) {
542         if (ch == s[slen])
543             ++res;
544     }
545     return res;
546 }
547 
548 static int
do_sgl(struct opts_t * op,const char * opt,const char * buf)549 do_sgl(struct opts_t * op, const char * opt, const char * buf)
550 {
551     int k, len, res, got;
552 
553     len = (int)strlen(buf);
554     if ((('-' == buf[0]) && (1 == len)) || ((len > 1) && ('@' == buf[0]))) {
555         res = file_to_sgl(((len > 1) ? (buf + 1) : buf), fixed_sgl, &got,
556                           MAX_FIXED_SGL_ELEMS);
557         if (res) {
558             pr2serr("bad argument to '%s'\n", opt);
559             return SG_LIB_SYNTAX_ERROR;
560         }
561     } else if (num_chs_in_str(buf, len, ',') > 0) {
562         res = cl_to_sgl(buf, fixed_sgl, &got, MAX_FIXED_SGL_ELEMS);
563         if (res) {
564             pr2serr("bad argument to '%s'\n", opt);
565             return SG_LIB_SYNTAX_ERROR;
566         }
567     } else {
568         pr2serr("bad argument to '%s', need at least one LBA,NUM pair\n",
569                  opt);
570         return SG_LIB_SYNTAX_ERROR;
571     }
572     op->in_sgl = fixed_sgl;
573     op->in_sgl_elems = got;
574     op->out_sgl = fixed_sgl;
575     op->out_sgl_elems = got;
576     if (op->verbose > 3) {
577         pr2serr("scatter-gather list (%d elements):\n", op->in_sgl_elems);
578         for (k = 0; k < op->in_sgl_elems; ++k)
579             pr2serr("  lba: 0x%" PRIx64 ", number: 0x%" PRIx32 "\n",
580                     op->in_sgl[k].lba, op->in_sgl[k].num);
581     }
582     return 0;
583 }
584 
585 
586 int
main(int argc,char * argv[])587 main(int argc, char * argv[])
588 {
589     int c, k, n, fd, flags, blk_sz, cont, err;
590     uint32_t delay;
591     int64_t i64, num_blks;
592     uint64_t tc;
593     int req_abort = 0;
594     int req_all_toks = 0;
595     int do_block = 0;
596     int do_hex = 0;
597     int do_info = 0;
598     int do_poll = 0;
599     int req_pt = 0;
600     int do_receive = 0;
601     int do_size = 0;
602     int req_wut = 0;
603     int ret = 0;
604     struct opts_t ops;
605     struct flags_t iflag, oflag;
606     struct dev_info_t ids, ods, o2ds;
607     struct sg_simple_inquiry_resp sir;
608     struct rrti_resp_t rrti_rsp;
609     struct opts_t * op;
610     char * np;
611     const char * sglp = NULL;
612     char b[80];
613     char bb[80];
614     unsigned char rt[512];
615 
616     state_init(&ops, &iflag, &oflag, &ids, &ods, &o2ds);
617     op = &ops;
618     memset(&sir, 0, sizeof(sir));
619     memset(&rrti_rsp, 0, sizeof(rrti_rsp));
620 
621     while (1) {
622         int option_index = 0;
623 
624         c = getopt_long(argc, argv, "AaBDhHiIl:O:pP:r:Rst:T:vVw:y",
625                         long_options, &option_index);
626         if (c == -1)
627             break;
628 
629         switch (c) {
630         case 'A':
631             ++req_abort;
632             break;
633         case 'a':
634             ++req_all_toks;
635             break;
636         case 'B':
637             ++do_block;
638             break;
639         case 'D':
640             ++op->oflagp->del_tkn;
641             break;
642         case 'h':
643         case '?':
644             usage();
645             return 0;
646         case 'H':
647             ++do_hex;
648             break;
649         case 'i':
650             ++do_info;
651             break;
652         case 'I':
653             ++op->iflagp->immed;
654             ++op->oflagp->immed;
655             break;
656         case 'l':
657             i64 = sg_get_llnum(optarg);
658             if (-1 == i64) {
659                 pr2serr("bad argument to 'list_id='\n");
660                 return SG_LIB_SYNTAX_ERROR;
661             }
662             if (i64 > UINT32_MAX) {
663                 pr2serr("argument to 'list_id=' too big for 32 bits\n");
664                 return SG_LIB_SYNTAX_ERROR;
665             }
666             op->list_id = (uint32_t)i64;
667             op->list_id_given = 1;
668             break;
669         case 'O':
670             op->offset_in_rod = sg_get_llnum(optarg);
671             if (-1LL == op->offset_in_rod) {
672                 pr2serr("bad argument to '--oir='\n");
673                 return SG_LIB_SYNTAX_ERROR;
674             }
675             break;
676         case 'p':
677             ++do_poll;
678             break;
679         case 'P':       /* takes gather list as argument */
680             if (req_pt) {
681                 pr2serr("Using two --pt=GL options is contradictory\n");
682                 return SG_LIB_SYNTAX_ERROR;
683             }
684             ++req_pt;
685             sglp = optarg;
686             break;
687         case 'r':
688             if (op->rtf[0]) {
689                 pr2serr("Can only use --rtf=RTF once for ROD Token "
690                         "filename\n");
691                 return SG_LIB_SYNTAX_ERROR;
692             }
693             if (optarg && (0 == strlen(optarg))) {
694                 pr2serr("--rtf= needs a non-blank argument (a filename)\n");
695                 return SG_LIB_SYNTAX_ERROR;
696             }
697             strncpy(op->rtf, optarg, INOUTF_SZ - 1);
698             break;
699         case 'R':
700             ++do_receive;
701             break;
702         case 's':
703             ++do_size;
704             break;
705         case 't':
706             if (0 == strncmp("pit-def", optarg, 7))
707                 op->rod_type = RODT_PIT_DEF;
708             else if (0 == strncmp("pit-vuln", optarg, 8))
709                 op->rod_type = RODT_PIT_VULN;
710             else if (0 == strncmp("pit-pers", optarg, 8))
711                 op->rod_type = RODT_PIT_PERS;
712             else if (0 == strncmp("pit-any", optarg, 7))
713                 op->rod_type = RODT_PIT_ANY;
714             else if (0 == strncmp("zero", optarg, 4))
715                 op->rod_type = RODT_BLK_ZERO;
716             else {
717                 i64 = sg_get_llnum(optarg);
718                 if (-1 == i64) {
719                     pr2serr("bad argument to '--rtype='; can give (hex) "
720                             "number, 'pit-def', 'pit-vuln',\n");
721                     pr2serr("'pit-pers', 'pit-any' or 'zero'\n");
722                     return SG_LIB_SYNTAX_ERROR;
723                 }
724                 if (i64 > UINT32_MAX) {
725                     pr2serr("'rtype=' argument exceeds 32 bits\n");
726                     return SG_LIB_SYNTAX_ERROR;
727                 }
728                 op->rod_type = (uint32_t)i64;
729             }
730             ++op->rod_type_given;
731             break;
732         case 'T':
733             n = sg_get_num(optarg);
734             if (-1 == n) {
735                 pr2serr("bad argument to '--timeout='\n");
736                 return SG_LIB_SYNTAX_ERROR;
737             }
738             op->inactivity_to = n;
739             np = strchr(optarg, ',');
740             if (np) {
741                 op->timeout_xcopy = sg_get_num(++np);
742                 if (-1 == op->timeout_xcopy) {
743                     pr2serr("bad argument to '--timeout=ok,xxx'\n");
744                     return SG_LIB_SYNTAX_ERROR;
745                 }
746             }
747             break;
748         case 'v':
749             ++op->verbose;
750             break;
751         case 'V':
752             pr2serr("version: %s\n", ddptctl_version_str);
753             return 0;
754         case 'w':       /* takes scatter list as argument */
755             if (req_wut) {
756                 pr2serr("Using two --wut=SL options is contradictory\n");
757                 return SG_LIB_SYNTAX_ERROR;
758             }
759             ++req_wut;
760             sglp = optarg;
761             break;
762         case 'y':
763             ++op->o_readonly;
764             break;
765         default:
766             pr2serr("unrecognised option code 0x%x ??\n", c);
767             usage();
768             return SG_LIB_SYNTAX_ERROR;
769         }
770     }
771     if (optind < argc) {
772         if ('\0' == op->idip->fn[0]) {
773             strncpy(op->idip->fn, argv[optind], INOUTF_SZ - 1);
774             strncpy(op->odip->fn, argv[optind], INOUTF_SZ - 1);
775             ++optind;
776         }
777         if (optind < argc) {
778             for (; optind < argc; ++optind)
779                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
780             usage();
781             return SG_LIB_SYNTAX_ERROR;
782         }
783     }
784     if ('\0' == op->idip->fn[0]) {
785         if (! op->rtf[0]) {
786             pr2serr("missing device name!\n");
787             usage();
788             return SG_LIB_SYNTAX_ERROR;
789         }
790     }
791 
792     op->odx_request = ODX_REQ_NONE;
793     k = 0;
794     if (req_abort)
795         ++k;
796     if (req_all_toks)
797         ++k;
798     if (do_poll) {
799         ++k;
800         op->odx_request = ODX_READ_INTO_RODS;
801     }
802     if (req_pt) {
803         ++k;
804         op->odx_request = ODX_READ_INTO_RODS;
805     }
806     if (do_receive) {
807         ++k;
808         op->odx_request = ODX_READ_INTO_RODS;
809     }
810     if (req_wut) {
811         ++k;
812         op->odx_request = ODX_WRITE_FROM_RODS;
813     }
814     if (k > 1) {
815         pr2serr("Can only have one of --abort, --all_toks, --poll, --pt=, "
816                 "--receive and --wut=\n");
817         return SG_LIB_SYNTAX_ERROR;
818     }
819     if (do_info && (1 == k)) {
820         pr2serr("--info cannot be used with an ODX command option (e.g. "
821                 "--pt=)\n");
822         return SG_LIB_SYNTAX_ERROR;
823     }
824     if (1 == k) {
825         if ('\0' == op->idip->fn[0]) {
826             pr2serr("need a DEVICE (e.g. /dev/sg3) to send command to\n");
827             return SG_LIB_SYNTAX_ERROR;
828         }
829         if (! (req_all_toks || op->list_id_given))
830             op->list_id = DEF_LID4_LID;
831     }
832     if (op->rtf[0]) {
833         if (do_info) {
834             if (op->idip->fn[0])
835                 pr2serr("Ignore device name [%s] and decode RTF\n",
836                         op->idip->fn);
837             return odx_rt_info(op);
838         } else if ('\0' == op->idip->fn[0])
839             return odx_rt_info(op);
840     }
841     /* If present, this will cause the ROD's size to be appended to the
842      * corresponding ROD Token placed in the RTF file (big endian, 8 byte) */
843     np = getenv(ODX_RTF_LEN);
844     if (np)
845         ++op->rtf_len_add;
846 
847     if (req_pt || req_wut) {
848         ret = do_sgl(op, (req_pt ? "--pt=" : "--wut="), sglp);
849         if (ret)
850             return ret;
851     }
852     op->idip->d_type = do_block ? FT_BLOCK : FT_PT;
853     if (op->idip->d_type & FT_PT) {
854         fd = pt_open_if(op, &sir);
855         if (-1 == fd) {
856             ret = SG_LIB_FILE_ERROR;
857             goto clean_up;
858         } else if (fd < -1) {
859             ret = SG_LIB_CAT_OTHER;
860             goto clean_up;
861         }
862         op->idip->fd = fd;
863         op->odip->fd = fd;
864     } else if (op->idip->d_type & FT_BLOCK) {
865         flags = O_RDONLY;
866         fd = open(op->idip->fn, flags);
867         if (fd < 0) {
868             pr2serr("could not open %s for reading: %s\n", op->idip->fn,
869                     safe_strerror(errno));
870             ret = SG_LIB_FILE_ERROR;
871             goto clean_up;
872         }
873         op->idip->fd = fd;
874         op->odip->fd = fd;
875     } else {
876         pr2serr("expecting to open a file but nothing found\n");
877         ret = SG_LIB_CAT_OTHER;
878         goto clean_up;
879     }
880 
881     if (op->odx_request != ODX_REQ_NONE) {
882         if (('\0' == op->rtf[0]) &&
883             (ODX_WRITE_FROM_RODS == op->odx_request) &&
884             (RODT_BLK_ZERO != op->rod_type)) {
885             pr2serr("--wut= needs ROD token file but no --rtf=RTF\n");
886             ret = SG_LIB_FILE_ERROR;
887             goto clean_up;
888         }
889         ret = open_rtf(op);
890         if (ret) {
891             ret = SG_LIB_FILE_ERROR;
892             goto clean_up;
893         }
894     }
895 
896     if (req_abort) {
897         ret = do_copy_abort(op);
898         if (ret)
899             goto clean_up;
900     } else if (req_all_toks) {
901         ret = report_all_toks(op, op->idip, do_hex);
902         if (ret)
903             goto clean_up;
904     } else if (do_poll) {
905         do {
906             ret = do_rrti(op, DDPT_ARG_IN, &rrti_rsp, op->verbose);
907             if (ret)
908                 goto clean_up;
909             cont = ((rrti_rsp.cstat >= 0x10) && (rrti_rsp.cstat <= 0x12));
910             if (cont) {
911                 delay = rrti_rsp.esu_del;
912                 if ((delay < 0xfffffffe) && (delay > 0)) {
913                     if (op->verbose > 1)
914                         pr2serr("using copy manager recommended delay of %"
915                                 PRIu32 " milliseconds\n", delay);
916                 } else {
917                     delay = DEF_ODX_POLL_DELAY_MS;
918                     if (op->verbose > 1)
919                         pr2serr("using default for poll delay\n");
920                 }
921                 if (delay)
922                     sleep_ms(delay);
923             }
924         } while (cont);
925         sg_get_opcode_sa_name(THIRD_PARTY_COPY_OUT_CMD, rrti_rsp.for_sa, 0,
926                               (int)sizeof(b), b);
927         printf("RRTI for %s: %s\n", b,
928                cpy_op_status_str(rrti_rsp.cstat, bb, sizeof(bb)));
929         printf("  transfer count of %" PRIu64 " [0x%" PRIx64 "]\n",
930                rrti_rsp.tc, rrti_rsp.tc);
931         if ((SA_POP_TOK == rrti_rsp.for_sa) && (rrti_rsp.rt_len > 0)) {
932             ret = write_to_rtf(op, &rrti_rsp);
933             if (ret)
934                 goto clean_up;
935         }
936     } else if (req_pt) {
937         op->odx_request = ODX_READ_INTO_RODS;
938         num_blks = count_sgl_blocks(op->in_sgl, op->in_sgl_elems);
939         if ((ret = do_pop_tok(op, 0, num_blks, 0, op->verbose)))
940             goto clean_up;
941         else if (op->iflagp->immed)
942             goto clean_up;
943         if ((ret = process_after_poptok(op, &tc, op->verbose)))
944             goto clean_up;
945         printf("PT completes with a transfer count of %" PRIu64 " [0x%"
946                PRIx64 "]\n", tc, tc);
947         if (op->rtf_fd < 0) {   /* ROD Token not sent to file, so ... */
948             /* dummy up rrti_rsp object and write to DEF_ROD_TOK_FILE */
949             rrti_rsp.rt_len = 512;
950             get_local_rod_tok(rrti_rsp.rod_tok, rrti_rsp.rt_len);
951             ret = write_to_rtf(op, &rrti_rsp);
952             if (ret)
953                 goto clean_up;
954         }
955         goto clean_up;
956     } else if (do_receive) {
957         ret = do_rrti(op, DDPT_ARG_IN, &rrti_rsp, op->verbose);
958         if (ret)
959             goto clean_up;
960         sg_get_opcode_sa_name(THIRD_PARTY_COPY_OUT_CMD, rrti_rsp.for_sa, 0,
961                               (int)sizeof(b), b);
962         printf("RRTI for %s: %s\n", b,
963                cpy_op_status_str(rrti_rsp.cstat, bb, sizeof(bb)));
964         printf("  transfer count of %" PRIu64 " [0x%" PRIx64 "]\n",
965                rrti_rsp.tc, rrti_rsp.tc);
966         if ((SA_POP_TOK == rrti_rsp.for_sa) && (rrti_rsp.rt_len > 0)) {
967             ret = write_to_rtf(op, &rrti_rsp);
968             if (ret)
969                 goto clean_up;
970         }
971     } else if (req_wut) {
972         op->odx_request = ODX_WRITE_FROM_RODS;
973         memset(rt, 0, sizeof(rt));
974         if (RODT_BLK_ZERO == op->rod_type) {
975             if (op->verbose > 1)
976                 pr2serr("  configure for block device zero ROD Token\n");
977             rt[0] = (unsigned char)((RODT_BLK_ZERO >> 24) & 0xff);
978             rt[1] = (unsigned char)((RODT_BLK_ZERO >> 16) & 0xff);
979             rt[2] = (unsigned char)((RODT_BLK_ZERO >> 8) & 0xff);
980             rt[3] = (unsigned char)(RODT_BLK_ZERO & 0xff);
981             rt[6] = (unsigned char)((ODX_ROD_TOK_LEN_FLD >> 8) & 0xff);
982             rt[7] = (unsigned char)(ODX_ROD_TOK_LEN_FLD & 0xff);
983         } else {
984             ret = read(op->rtf_fd, rt, sizeof(rt));
985             if (ret < 0) {
986                 err = errno;
987                 pr2serr("could not read '%s': %s\n", op->rtf,
988                         safe_strerror(err));
989                 goto clean_up;
990             }
991             if (ret < (int)sizeof(rt))
992                 pr2serr("unable to read %d bytes from '%s', only got %d "
993                         "bytes\n", (int)sizeof(rt), op->rtf, ret);
994         }
995         num_blks = count_sgl_blocks(op->out_sgl, op->out_sgl_elems);
996         if ((ret = do_wut(op, rt, 0, num_blks, op->offset_in_rod,
997                           1 /* assume more left */, 0, op->verbose)))
998             goto clean_up;
999         else if (op->oflagp->immed)
1000             goto clean_up;
1001         if ((ret = process_after_wut(op, &tc, op->verbose)))
1002             goto clean_up;
1003         printf("WUT completes with a transfer count of %" PRIu64 " [0x%"
1004                PRIx64 "]\n", tc, tc);
1005     } else if (do_info || do_size) {
1006         if (op->idip->d_type & FT_PT) {
1007             ret = pt_read_capacity(op, DDPT_ARG_IN, &num_blks, &blk_sz);
1008             if (ret) {
1009                 if (SG_LIB_CAT_UNIT_ATTENTION == ret) {
1010                     if (op->verbose)
1011                         pr2serr("Unit attention (readcap), continuing\n");
1012                     ret = pt_read_capacity(op, DDPT_ARG_IN,
1013                                            &num_blks, &blk_sz);
1014                     if (ret) {
1015                         if (0 == sir.peripheral_type)
1016                             pr2serr("read capacity failed, perhaps because "
1017                                     "non-disk device [pdt=%d]\n",
1018                                     sir.peripheral_type);
1019                         goto clean_up;
1020                     }
1021                 }
1022             }
1023             print_blk_sizes(op->idip->fn, "readcap", num_blks, blk_sz, 0);
1024             if (do_info) {
1025                 if (0x8 & sir.byte_5) {
1026                     printf("3PC (third party copy) bit set in standard "
1027                            "INQUIRY response\n");
1028                     printf(" Third Party Copy VPD page:\n");
1029                     print_3pc_vpd(op, 0);
1030                 } else {
1031                     printf("3PC (third party copy) bit clear in standard "
1032                            "INQUIRY response\n");
1033                     printf("  so %s [pdt=0x%x] does not seem to support "
1034                            "XCOPY\n", op->idip->fn, sir.peripheral_type);
1035                 }
1036             }
1037         } else if (op->idip->d_type & FT_BLOCK) {
1038             ret = get_blkdev_capacity(op, DDPT_ARG_IN, &num_blks, &blk_sz);
1039             if (ret)
1040                 goto clean_up;
1041             print_blk_sizes(op->idip->fn, "block", num_blks, blk_sz, 0);
1042         } else {
1043             num_blks = 0;
1044             blk_sz = 0;
1045             printf("unable to print capacity information about device\n");
1046         }
1047     } else
1048         printf("Expecting to see an option; try again with '-h'\n");
1049 
1050 clean_up:
1051     if ((req_pt || req_wut) && op->iflagp->immed && (0 == ret))
1052         pr2serr("Started ODX %s command in immediate mode.\nUser may need "
1053                 "--list_id=%" PRIu32 " on following invocation with "
1054                 "--receive or\n--poll for completion\n",
1055                 (req_pt ? "Populate Token" : "Write Using Token"),
1056                 op->list_id);
1057     if (op->idip->fd >= 0) {
1058         if (op->idip->d_type & FT_PT)
1059             pt_close(op->idip->fd);
1060         else if (op->idip->d_type & FT_BLOCK)
1061             close(op->idip->fd);
1062     }
1063     if (op->rtf_fd >= 0)
1064         close(op->rtf_fd);
1065     if (ret) {
1066         if (ret > 0)
1067             print_exit_status_msg("Exit status", ret, 0);
1068         else if (ret < 0) {
1069             pr2serr("Some error occurred\n");
1070             ret = 1;
1071         }
1072     }
1073     return ret;
1074 }
1075