xref: /aosp_15_r20/external/sg3_utils/src/sg_copy_results.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2011-2018 Hannes Reinecke, SUSE Labs
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  */
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <getopt.h>
16 #define __STDC_FORMAT_MACROS 1
17 #include <inttypes.h>
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "sg_lib.h"
23 #include "sg_cmds_basic.h"
24 #include "sg_cmds_extra.h"
25 #include "sg_unaligned.h"
26 #include "sg_pr2serr.h"
27 
28 /*
29  * A utility program for the Linux OS SCSI subsystem.
30  *  Copyright (C) 2004-2010 D. Gilbert
31  * This program is free software; you can redistribute it and/or modify
32  * it under the terms of the GNU General Public License as published by
33  * the Free Software Foundation; either version 2, or (at your option)
34  * any later version.
35  *
36  * SPDX-License-Identifier: GPL-2.0-or-later
37  *
38  * This program issues the SCSI command RECEIVE COPY RESULTS to a given
39  * SCSI device.
40  * It sends the command with the service action passed as the sa argument,
41  * and the optional list identifier passed as the list_id argument.
42  */
43 
44 static const char * version_str = "1.23 20180625";
45 
46 
47 #define MAX_XFER_LEN 10000
48 
49 
50 #define ME "sg_copy_results: "
51 
52 #define EBUFF_SZ 256
53 
54 struct descriptor_type {
55     int code;
56     char desc[124];
57 };
58 
59 struct descriptor_type target_descriptor_codes[] = {
60     { 0xe0, "Fibre Channel N_Port_Name"},
61     { 0xe1, "Fibre Channel N_port_ID"},
62     { 0xe2, "Fibre Channesl N_port_ID with N_Port_Name checking"},
63     { 0xe3, "Parallel Interface T_L" },
64     { 0xe4, "Identification descriptor" },
65     { 0xe5, "IPv4" },
66     { 0xe6, "Alias" },
67     { 0xe7, "RDMA" },
68     { 0xe8, "IEEE 1395 EUI-64" },
69     { 0xe9, "SAS Serial SCSI Protocol" },
70     { 0xea, "IPv6" },
71     { 0xeb, "IP Copy Service" },
72     { -1, "" }
73 };
74 
75 struct descriptor_type segment_descriptor_codes [] = {
76     { 0x00, "Copy from block device to stream device" },
77     { 0x01, "Copy from stream device to block device" },
78     { 0x02, "Copy from block device to block device" },
79     { 0x03, "Copy from stream device to stream device" },
80     { 0x04, "Copy inline data to stream device" },
81     { 0x05, "Copy embedded data to stream device" },
82     { 0x06, "Read from stream device and discard" },
83     { 0x07, "Verify block or stream device operation" },
84     { 0x08, "Copy block device with offset to stream device" },
85     { 0x09, "Copy stream device to block device with offset" },
86     { 0x0A, "Copy block device with offset to block device with offset" },
87     { 0x0B, "Copy from block device to stream device "
88       "and hold a copy of processed data for the application client" },
89     { 0x0C, "Copy from stream device to block device "
90       "and hold a copy of processed data for the application client" },
91     { 0x0D, "Copy from block device to block device "
92       "and hold a copy of processed data for the application client" },
93     { 0x0E, "Copy from stream device to stream device "
94       "and hold a copy of processed data for the application client" },
95     { 0x0F, "Read from stream device "
96       "and hold a copy of processed data for the application client" },
97     { 0x10, "Write filemarks to sequential-access device" },
98     { 0x11, "Space records or filemarks on sequential-access device" },
99     { 0x12, "Locate on sequential-access device" },
100     { 0x13, "Image copy from sequential-access device to sequential-access "
101             "device" },
102     { 0x14, "Register persistent reservation key" },
103     { 0x15, "Third party persistent reservations source I_T nexus" },
104     { -1, "" }
105 };
106 
107 
108 static void
scsi_failed_segment_details(uint8_t * rcBuff,unsigned int rcBuffLen)109 scsi_failed_segment_details(uint8_t *rcBuff, unsigned int rcBuffLen)
110 {
111     int senseLen;
112     unsigned int len;
113     char senseBuff[1024];
114 
115     if (rcBuffLen < 4) {
116         pr2serr("  <<not enough data to procedd report>>\n");
117         return;
118     }
119     len = sg_get_unaligned_be32(rcBuff + 0);
120     if (len + 4 > rcBuffLen) {
121         pr2serr("  <<report len %d > %d too long for internal buffer, output "
122                 "truncated\n", len, rcBuffLen);
123     }
124     if (len < 52) {
125         pr2serr("  <<no segment details, response data length %d\n", len);
126         return;
127     }
128     printf("Receive copy results (failed segment details):\n");
129     printf("    Extended copy command status: %d\n", rcBuff[56]);
130     senseLen = sg_get_unaligned_be16(rcBuff + 58);
131     sg_get_sense_str("    ", &rcBuff[60], senseLen, 0, 1024, senseBuff);
132     printf("%s", senseBuff);
133 }
134 
135 static void
scsi_copy_status(uint8_t * rcBuff,unsigned int rcBuffLen)136 scsi_copy_status(uint8_t *rcBuff, unsigned int rcBuffLen)
137 {
138     unsigned int len;
139 
140     if (rcBuffLen < 4) {
141         pr2serr("  <<not enough data to proceed report>>\n");
142         return;
143     }
144     len = sg_get_unaligned_be32(rcBuff + 0);
145     if (len + 4 > rcBuffLen) {
146         pr2serr("  <<report len %d > %d too long for internal buffer, output "
147                 "truncated\n", len, rcBuffLen);
148     }
149     printf("Receive copy results (copy status):\n");
150     printf("    Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No");
151     printf("    Copy manager status: ");
152     switch (rcBuff[4] & 0x7f) {
153     case 0:
154         printf("Operation in progress\n");
155         break;
156     case 1:
157         printf("Operation completed without errors\n");
158         break;
159     case 2:
160         printf("Operation completed with errors\n");
161         break;
162     default:
163         printf("Unknown/Reserved\n");
164         break;
165     }
166     printf("    Segments processed: %u\n", sg_get_unaligned_be16(rcBuff + 5));
167     printf("    Transfer count units: %u\n", rcBuff[7]);
168     printf("    Transfer count: %u\n", sg_get_unaligned_be32(rcBuff + 8));
169 }
170 
171 static void
scsi_operating_parameters(uint8_t * rcBuff,unsigned int rcBuffLen)172 scsi_operating_parameters(uint8_t *rcBuff, unsigned int rcBuffLen)
173 {
174     unsigned int len, n;
175 
176     len = sg_get_unaligned_be32(rcBuff + 0);
177     if (len + 4 > rcBuffLen) {
178         pr2serr("  <<report len %d > %d too long for internal buffer, output "
179                 "truncated\n", len, rcBuffLen);
180     }
181     printf("Receive copy results (report operating parameters):\n");
182     printf("    Supports no list identifier (SNLID): %s\n",
183            rcBuff[4] & 1 ? "yes" : "no");
184     n = sg_get_unaligned_be16(rcBuff + 8);
185     printf("    Maximum target descriptor count: %u\n", n);
186     n = sg_get_unaligned_be16(rcBuff + 10);
187     printf("    Maximum segment descriptor count: %u\n", n);
188     n = sg_get_unaligned_be32(rcBuff + 12);
189     printf("    Maximum descriptor list length: %u bytes\n", n);
190     n = sg_get_unaligned_be32(rcBuff + 16);
191     printf("    Maximum segment length: %u bytes\n", n);
192     n = sg_get_unaligned_be32(rcBuff + 20);
193     if (n == 0) {
194         printf("    Inline data not supported\n");
195     } else {
196         printf("    Maximum inline data length: %u bytes\n", n);
197     }
198     n = sg_get_unaligned_be32(rcBuff + 24);
199     printf("    Held data limit: %u bytes\n", n);
200     n = sg_get_unaligned_be32(rcBuff + 28);
201     printf("    Maximum stream device transfer size: %u bytes\n", n);
202     n = sg_get_unaligned_be16(rcBuff + 34);
203     printf("    Total concurrent copies: %u\n", n);
204     printf("    Maximum concurrent copies: %u\n", rcBuff[36]);
205     if (rcBuff[37] > 30)
206         printf("    Data segment granularity: 2**%u bytes\n", rcBuff[37]);
207     else
208         printf("    Data segment granularity: %u bytes\n",
209                (unsigned int)(1 << rcBuff[37]));
210     if (rcBuff[38] > 30)
211         printf("    Inline data granularity: %u bytes\n", rcBuff[38]);
212     else
213         printf("    Inline data granularity: %u bytes\n",
214                (unsigned int)(1 << rcBuff[38]));
215     if (rcBuff[39] > 30)
216         printf("    Held data granularity: 2**%u bytes\n", rcBuff[39]);
217     else
218         printf("    Held data granularity: %u bytes\n",
219                (unsigned int)(1 << rcBuff[39]));
220 
221     printf("    Implemented descriptor list:\n");
222     for (n = 0; n < rcBuff[43]; n++) {
223         int code = rcBuff[44 + n];
224 
225         if (code < 0x16) {
226             struct descriptor_type *seg_desc = segment_descriptor_codes;
227             while (strlen(seg_desc->desc)) {
228                 if (seg_desc->code == code)
229                     break;
230                 seg_desc++;
231             }
232             printf("        Segment descriptor 0x%02x: %s\n", code,
233                    strlen(seg_desc->desc) ? seg_desc->desc : "Reserved");
234         } else if (code < 0xc0) {
235             printf("        Segment descriptor 0x%02x: Reserved\n", code);
236         } else if (code < 0xe0) {
237             printf("        Vendor specific descriptor 0x%02x\n", code);
238         } else {
239             struct descriptor_type *tgt_desc = target_descriptor_codes;
240 
241             while (strlen(tgt_desc->desc)) {
242                 if (tgt_desc->code == code)
243                     break;
244                 tgt_desc++;
245             }
246             printf("        Target descriptor 0x%02x: %s\n", code,
247                    strlen(tgt_desc->desc) ? tgt_desc->desc : "Reserved");
248         }
249     }
250     printf("\n");
251 }
252 
253 static struct option long_options[] = {
254         {"failed", no_argument, 0, 'f'},
255         {"help", no_argument, 0, 'h'},
256         {"hex", no_argument, 0, 'H'},
257         {"list_id", required_argument, 0, 'l'},
258         {"list-id", required_argument, 0, 'l'},
259         {"params", no_argument, 0, 'p'},
260         {"readonly", no_argument, 0, 'R'},
261         {"receive", no_argument, 0, 'r'},
262         {"status", no_argument, 0, 's'},
263         {"verbose", no_argument, 0, 'v'},
264         {"version", no_argument, 0, 'V'},
265         {"xfer_len", required_argument, 0, 'x'},
266         {0, 0, 0, 0},
267 };
268 
269 static void
usage()270 usage()
271 {
272   pr2serr("Usage: "
273           "sg_copy_results [--failed|--params|--receive|--status] [--help]\n"
274           "                       [--hex] [--list_id=ID] [--readonly] "
275           "[--verbose]\n"
276           "                       [--version] [--xfer_len=BTL] DEVICE\n"
277           "  where:\n"
278           "    --failed|-f          use FAILED SEGMENT DETAILS service "
279           "action\n"
280           "    --help|-h            print out usage message\n"
281           "    --hex|-H             print out response buffer in hex\n"
282           "    --list_id=ID|-l ID   list identifier (default: 0)\n"
283           "    --params|-p          use OPERATING PARAMETERS service "
284           "action\n"
285           "    --readonly|-R        open DEVICE read-only (def: read-write)\n"
286           "    --receive|-r         use RECEIVE DATA service action\n"
287           "    --status|-s          use COPY STATUS service action\n"
288           "    --verbose|-v         increase verbosity\n"
289           "    --version|-V         print version string then exit\n"
290           "    --xfer_len=BTL|-x BTL    byte transfer length (< 10000) "
291           "(default:\n"
292           "                             520 bytes)\n\n"
293           "Performs a SCSI RECEIVE COPY RESULTS command. Returns the "
294           "response as\nspecified by the service action parameters.\n"
295           );
296 }
297 
298 static const char * rec_copy_name_arr[] = {
299     "Receive copy status(LID1)",
300     "Receive copy data(LID1)",
301     "Receive copy [0x2]",
302     "Receive copy operating parameters",
303     "Receive copy failure details(LID1)",
304 };
305 
306 int
main(int argc,char * argv[])307 main(int argc, char * argv[])
308 {
309     bool do_hex = false;
310     bool o_readonly = false;
311     bool verbose_given = false;
312     bool version_given = false;
313     int res, c, k;
314     int ret = 1;
315     int sa = 3;
316     int sg_fd = -1;
317     int verbose = 0;
318     int xfer_len = 520;
319     uint32_t list_id = 0;
320     const char * cp;
321     uint8_t * cpResultBuff = NULL;
322     uint8_t * free_cprb = NULL;
323     const char * device_name = NULL;
324     char file_name[256];
325 
326     memset(file_name, 0, sizeof file_name);
327     while (1) {
328         int option_index = 0;
329 
330         c = getopt_long(argc, argv, "fhHl:prRsvVx:", long_options,
331                         &option_index);
332         if (c == -1)
333             break;
334 
335         switch (c) {
336         case 'f':
337             sa = 4;
338             break;
339         case 'H':
340             do_hex = true;
341             break;
342         case 'h':
343         case '?':
344             usage();
345             return 0;
346         case 'l':
347             k = sg_get_num(optarg);
348             if (-1 == k) {
349                 pr2serr("bad argument to '--list_id'\n");
350                 return SG_LIB_SYNTAX_ERROR;
351             }
352             list_id = (uint32_t)k;
353             break;
354         case 'p':
355             sa = 3;
356             break;
357         case 'r':
358             sa = 1;
359             break;
360         case 'R':
361             o_readonly = true;
362             break;
363         case 's':
364             sa = 0;
365             break;
366         case 'v':
367             ++verbose;
368             verbose_given = true;
369             break;
370         case 'V':
371             version_given = true;
372             break;
373         case 'x':
374             xfer_len = sg_get_num(optarg);
375             if (-1 == xfer_len) {
376                 pr2serr("bad argument to '--xfer_len'\n");
377                 return SG_LIB_SYNTAX_ERROR;
378             }
379             break;
380         default:
381             pr2serr("unrecognised option code 0x%x ??\n", c);
382             usage();
383             return SG_LIB_SYNTAX_ERROR;
384         }
385     }
386     if (optind < argc) {
387         if (NULL == device_name) {
388             device_name = argv[optind];
389             ++optind;
390         }
391         if (optind < argc) {
392             for (; optind < argc; ++optind)
393                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
394             usage();
395             return SG_LIB_SYNTAX_ERROR;
396         }
397     }
398 
399 #ifdef DEBUG
400     pr2serr("In DEBUG mode, ");
401     if (verbose_given && version_given) {
402         pr2serr("but override: '-vV' given, zero verbose and continue\n");
403         verbose_given = false;
404         version_given = false;
405         verbose = 0;
406     } else if (! verbose_given) {
407         pr2serr("set '-vv'\n");
408         verbose = 2;
409     } else
410         pr2serr("keep verbose=%d\n", verbose);
411 #else
412     if (verbose_given && version_given)
413         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
414 #endif
415     if (version_given) {
416         pr2serr(ME "version: %s\n", version_str);
417         return 0;
418     }
419 
420     if (NULL == device_name) {
421         pr2serr("missing device name!\n\n");
422         usage();
423         return SG_LIB_SYNTAX_ERROR;
424     }
425     if (xfer_len >= MAX_XFER_LEN) {
426         pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
427                 MAX_XFER_LEN);
428         usage();
429         return SG_LIB_SYNTAX_ERROR;
430     }
431 
432     cpResultBuff = (uint8_t *)sg_memalign(xfer_len, 0, &free_cprb,
433                                           verbose > 3);
434     if (NULL == cpResultBuff) {
435             pr2serr(ME "out of memory\n");
436             return sg_convert_errno(ENOMEM);
437     }
438 
439     sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
440     if (sg_fd < 0) {
441         if (verbose)
442             pr2serr(ME "open error: %s: %s\n", device_name,
443                     safe_strerror(-sg_fd));
444         ret = sg_convert_errno(-sg_fd);
445         goto finish;
446     }
447 
448     if ((sa < 0) || (sa >= (int)SG_ARRAY_SIZE(rec_copy_name_arr)))
449         cp = "Out of range service action";
450     else
451         cp = rec_copy_name_arr[sa];
452     if (verbose)
453         pr2serr(ME "issue %s to device %s\n\t\txfer_len= %d (0x%x), list_id=%"
454                 PRIu32 "\n", cp, device_name, xfer_len, xfer_len, list_id);
455 
456     res = sg_ll_receive_copy_results(sg_fd, sa, list_id, cpResultBuff,
457                                      xfer_len, true, verbose);
458     ret = res;
459     if (res) {
460         char b[80];
461 
462         sg_get_category_sense_str(res, sizeof(b), b, verbose);
463         pr2serr("  SCSI %s failed: %s\n", cp, b);
464         goto finish;
465     }
466     if (do_hex) {
467         hex2stdout(cpResultBuff, xfer_len, 1);
468         goto finish;
469     }
470     switch (sa) {
471     case 4: /* Failed segment details */
472         scsi_failed_segment_details(cpResultBuff, xfer_len);
473         break;
474     case 3: /* Operating parameters */
475         scsi_operating_parameters(cpResultBuff, xfer_len);
476         break;
477     case 0: /* Copy status */
478         scsi_copy_status(cpResultBuff, xfer_len);
479         break;
480     default:
481         hex2stdout(cpResultBuff, xfer_len, 1);
482         break;
483     }
484 
485 finish:
486     if (free_cprb)
487         free(free_cprb);
488     if (sg_fd >= 0) {
489         res = sg_cmds_close_device(sg_fd);
490         if (res < 0) {
491             pr2serr(ME "close error: %s\n", safe_strerror(-res));
492             if (0 == ret)
493                 ret = sg_convert_errno(-res);
494         }
495     }
496     if (0 == verbose) {
497         if (! sg_if_can2stderr("sg_copy_results failed: ", ret))
498             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
499                     "more information\n");
500     }
501     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
502 }
503