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