Home | History | Annotate | Download | only in sg_write_buffer
      1 /*
      2  * Copyright (c) 1999-2018 Douglas Gilbert.
      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 
      8 /*
      9  * CONTENTS
     10  *    Some SCSI commands are executed in many contexts and hence began
     11  *    to appear in several sg3_utils utilities. This files centralizes
     12  *    some of the low level command execution code. In most cases the
     13  *    interpretation of the command response is left to the each
     14  *    utility.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <stdarg.h>
     20 #include <stdbool.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include "config.h"
     26 #endif
     27 
     28 #include "sg_lib.h"
     29 #include "sg_cmds_basic.h"
     30 #include "sg_pt.h"
     31 #include "sg_unaligned.h"
     32 
     33 /* Needs to be after config.h */
     34 #ifdef SG_LIB_LINUX
     35 #include <errno.h>
     36 #endif
     37 
     38 
     39 static const char * const version_str = "1.83 20180204";
     40 
     41 
     42 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
     43 #define EBUFF_SZ 256
     44 
     45 #define DEF_PT_TIMEOUT 60       /* 60 seconds */
     46 #define START_PT_TIMEOUT 120    /* 120 seconds == 2 minutes */
     47 #define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
     48 
     49 #define INQUIRY_CMD     0x12
     50 #define INQUIRY_CMDLEN  6
     51 #define REQUEST_SENSE_CMD 0x3
     52 #define REQUEST_SENSE_CMDLEN 6
     53 #define REPORT_LUNS_CMD 0xa0
     54 #define REPORT_LUNS_CMDLEN 12
     55 #define TUR_CMD  0x0
     56 #define TUR_CMDLEN  6
     57 
     58 #define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
     59 
     60 
     61 const char *
     62 sg_cmds_version()
     63 {
     64     return version_str;
     65 }
     66 
     67 #if defined(__GNUC__) || defined(__clang__)
     68 static int pr2ws(const char * fmt, ...)
     69         __attribute__ ((format (printf, 1, 2)));
     70 #else
     71 static int pr2ws(const char * fmt, ...);
     72 #endif
     73 
     74 
     75 static int
     76 pr2ws(const char * fmt, ...)
     77 {
     78     va_list args;
     79     int n;
     80 
     81     va_start(args, fmt);
     82     n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
     83     va_end(args);
     84     return n;
     85 }
     86 
     87 /* Returns file descriptor >= 0 if successful. If error in Unix returns
     88    negated errno. */
     89 int
     90 sg_cmds_open_device(const char * device_name, bool read_only, int verbose)
     91 {
     92     /* The following 2 lines are temporary. It is to avoid a NULL pointer
     93      * crash when an old utility is used with a newer library built after
     94      * the sg_warnings_strm cleanup */
     95     if (NULL == sg_warnings_strm)
     96         sg_warnings_strm = stderr;
     97 
     98     return scsi_pt_open_device(device_name, read_only, verbose);
     99 }
    100 
    101 /* Returns file descriptor >= 0 if successful. If error in Unix returns
    102    negated errno. */
    103 int
    104 sg_cmds_open_flags(const char * device_name, int flags, int verbose)
    105 {
    106     return scsi_pt_open_flags(device_name, flags, verbose);
    107 }
    108 
    109 /* Returns 0 if successful. If error in Unix returns negated errno. */
    110 int
    111 sg_cmds_close_device(int device_fd)
    112 {
    113     return scsi_pt_close_device(device_fd);
    114 }
    115 
    116 static const char * const pass_through_s = "pass-through";
    117 
    118 static int
    119 sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
    120                        const unsigned char * sbp, int slen, bool noisy,
    121                        int verbose, int * o_sense_cat)
    122 {
    123     int scat, got;
    124     bool n = false;
    125     bool check_data_in = false;
    126     char b[512];
    127 
    128     scat = sg_err_category_sense(sbp, slen);
    129     switch (scat) {
    130     case SG_LIB_CAT_NOT_READY:
    131     case SG_LIB_CAT_INVALID_OP:
    132     case SG_LIB_CAT_ILLEGAL_REQ:
    133     case SG_LIB_CAT_ABORTED_COMMAND:
    134     case SG_LIB_CAT_COPY_ABORTED:
    135     case SG_LIB_CAT_DATA_PROTECT:
    136     case SG_LIB_CAT_PROTECTION:
    137     case SG_LIB_CAT_NO_SENSE:
    138     case SG_LIB_CAT_MISCOMPARE:
    139         n = false;
    140         break;
    141     case SG_LIB_CAT_RECOVERED:
    142     case SG_LIB_CAT_MEDIUM_HARD:
    143         check_data_in = true;
    144 #if defined(__GNUC__)
    145 #if (__GNUC__ >= 7)
    146         __attribute__((fallthrough));
    147         /* FALL THROUGH */
    148 #endif
    149 #endif
    150     case SG_LIB_CAT_UNIT_ATTENTION:
    151     case SG_LIB_CAT_SENSE:
    152     default:
    153         n = noisy;
    154         break;
    155     }
    156     if (verbose || n) {
    157         if (leadin && (strlen(leadin) > 0))
    158             pr2ws("%s:\n", leadin);
    159         sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
    160                          sizeof(b), b);
    161         pr2ws("%s", b);
    162         if ((mx_di_len > 0) && (resid > 0)) {
    163             got = mx_di_len - resid;
    164             if ((verbose > 2) || check_data_in || (got > 0))
    165                 pr2ws("    %s requested %d bytes (data-in) but got %d "
    166                       "bytes\n", pass_through_s, mx_di_len, got);
    167         }
    168     }
    169     if (o_sense_cat)
    170         *o_sense_cat = scat;
    171     return -2;
    172 }
    173 
    174 /* This is a helper function used by sg_cmds_* implementations after the
    175  * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
    176  * sense data is found it is decoded and output to sg_warnings_strm (def:
    177  * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
    178  * "sense" category (may not be fatal), -1 for failed, 0, or a positive
    179  * number. If 'mx_di_len > 0' then asks pass-through for resid and returns
    180  * (mx_di_len - resid); otherwise returns 0. So for data-in it should return
    181  * the actual number of bytes received. For data-out (to device) or no data
    182  * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
    183  * output via 'o_sense_cat' pointer (if not NULL). Note that several sense
    184  * categories also have data in bytes received; -2 is still returned. */
    185 int
    186 sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
    187                      int pt_res, int mx_di_len, const unsigned char * sbp,
    188                      bool noisy, int verbose, int * o_sense_cat)
    189 {
    190     int got, cat, duration, slen, resid, resp_code, sstat;
    191     bool transport_sense;
    192     char b[1024];
    193 
    194     if (NULL == leadin)
    195         leadin = "";
    196     if (pt_res < 0) {
    197 #ifdef SG_LIB_LINUX
    198         if (verbose)
    199             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
    200                   safe_strerror(-pt_res));
    201         if ((-ENXIO == pt_res) && o_sense_cat) {
    202             if (verbose > 2)
    203                 pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
    204             *o_sense_cat = SG_LIB_CAT_NOT_READY;
    205             return -2;
    206         } else if (noisy && (0 == verbose))
    207             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
    208                   safe_strerror(-pt_res));
    209 #else
    210         if (noisy || verbose)
    211             pr2ws("%s: %s os error: %s\n", leadin, pass_through_s,
    212                   safe_strerror(-pt_res));
    213 #endif
    214         return -1;
    215     } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
    216         pr2ws("%s: bad %s setup\n", leadin, pass_through_s);
    217         return -1;
    218     } else if (SCSI_PT_DO_TIMEOUT == pt_res) {
    219         pr2ws("%s: %s timeout\n", leadin, pass_through_s);
    220         return -1;
    221     }
    222     if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
    223         pr2ws("      duration=%d ms\n", duration);
    224     resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
    225     slen = get_scsi_pt_sense_len(ptvp);
    226     switch ((cat = get_scsi_pt_result_category(ptvp))) {
    227     case SCSI_PT_RESULT_GOOD:
    228         if (sbp && (slen > 7)) {
    229             resp_code = sbp[0] & 0x7f;
    230             /* SBC referrals can have status=GOOD and sense_key=COMPLETED */
    231             if (resp_code >= 0x70) {
    232                 if (resp_code < 0x72) {
    233                     if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
    234                         sg_err_category_sense(sbp, slen);
    235                 } else if (resp_code < 0x74) {
    236                     if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
    237                         sg_err_category_sense(sbp, slen);
    238                 }
    239             }
    240         }
    241         if (mx_di_len > 0) {
    242             got = mx_di_len - resid;
    243             if ((verbose > 1) && (resid != 0))
    244                 pr2ws("    %s: %s requested %d bytes (data-in) but got %d "
    245                       "bytes\n", leadin, pass_through_s, mx_di_len, got);
    246             if (got >= 0)
    247                 return got;
    248             else {
    249                 if (verbose)
    250                     pr2ws("    %s: %s can't get negative bytes, say it got "
    251                           "none\n", leadin, pass_through_s);
    252                 return 0;
    253             }
    254         } else
    255             return 0;
    256     case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
    257         sstat = get_scsi_pt_status_response(ptvp);
    258         if (o_sense_cat) {
    259             switch (sstat) {
    260             case SAM_STAT_RESERVATION_CONFLICT:
    261                 *o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
    262                 return -2;
    263             case SAM_STAT_CONDITION_MET:
    264                 *o_sense_cat = SG_LIB_CAT_CONDITION_MET;
    265                 return -2;
    266             case SAM_STAT_BUSY:
    267                 *o_sense_cat = SG_LIB_CAT_BUSY;
    268                 return -2;
    269             case SAM_STAT_TASK_SET_FULL:
    270                 *o_sense_cat = SG_LIB_CAT_TS_FULL;
    271                 return -2;
    272             case SAM_STAT_ACA_ACTIVE:
    273                 *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
    274                 return -2;
    275             case SAM_STAT_TASK_ABORTED:
    276                 *o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
    277                 return -2;
    278             default:
    279                 break;
    280             }
    281         }
    282         if (verbose || noisy) {
    283             sg_get_scsi_status_str(sstat, sizeof(b), b);
    284             pr2ws("%s: scsi status: %s\n", leadin, b);
    285         }
    286         return -1;
    287     case SCSI_PT_RESULT_SENSE:
    288         return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
    289                                       noisy, verbose, o_sense_cat);
    290     case SCSI_PT_RESULT_TRANSPORT_ERR:
    291         if (verbose || noisy) {
    292             get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
    293             pr2ws("%s: transport: %s\n", leadin, b);
    294         }
    295 #ifdef SG_LIB_LINUX
    296         transport_sense = (slen > 0);
    297 #else
    298         transport_sense = ((SAM_STAT_CHECK_CONDITION ==
    299                             get_scsi_pt_status_response(ptvp)) && (slen > 0));
    300 #endif
    301         if (transport_sense)
    302             return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
    303                                           slen, noisy, verbose, o_sense_cat);
    304         else
    305             return -1;
    306     case SCSI_PT_RESULT_OS_ERR:
    307         if (verbose || noisy) {
    308             get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
    309             pr2ws("%s: os: %s\n", leadin, b);
    310         }
    311         return -1;
    312     default:
    313         pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s,
    314                cat);
    315         return -1;
    316     }
    317 }
    318 
    319 bool
    320 sg_cmds_is_nvme(const struct sg_pt_base * ptvp)
    321 {
    322     return pt_device_is_nvme(ptvp);
    323 }
    324 
    325 static struct sg_pt_base *
    326 create_pt_obj(const char * cname)
    327 {
    328     struct sg_pt_base * ptvp = construct_scsi_pt_obj();
    329     if (NULL == ptvp)
    330         pr2ws("%s: out of memory\n", cname);
    331     return ptvp;
    332 }
    333 
    334 static const char * const inquiry_s = "inquiry";
    335 
    336 static int
    337 sg_ll_inquiry_com(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
    338                   int mx_resp_len, int timeout_secs, int * residp,
    339                   bool noisy, int verbose)
    340 {
    341     int res, ret, k, sense_cat, resid;
    342     unsigned char inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
    343     unsigned char sense_b[SENSE_BUFF_LEN];
    344     unsigned char * up;
    345     struct sg_pt_base * ptvp;
    346 
    347     if (cmddt)
    348         inq_cdb[1] |= 0x2;
    349     if (evpd)
    350         inq_cdb[1] |= 0x1;
    351     inq_cdb[2] = (unsigned char)pg_op;
    352     /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */
    353     sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3);
    354     if (verbose) {
    355         pr2ws("    %s cdb: ", inquiry_s);
    356         for (k = 0; k < INQUIRY_CMDLEN; ++k)
    357             pr2ws("%02x ", inq_cdb[k]);
    358         pr2ws("\n");
    359     }
    360     if (resp && (mx_resp_len > 0)) {
    361         up = (unsigned char *)resp;
    362         up[0] = 0x7f;   /* defensive prefill */
    363         if (mx_resp_len > 4)
    364             up[4] = 0;
    365     }
    366     if (timeout_secs <= 0)
    367         timeout_secs = DEF_PT_TIMEOUT;
    368     ptvp = construct_scsi_pt_obj();
    369     if (NULL == ptvp) {
    370         pr2ws("%s: out of memory\n", __func__);
    371         if (residp)
    372             *residp = 0;
    373         return -1;
    374     }
    375     set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb));
    376     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
    377     set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
    378     res = do_scsi_pt(ptvp, sg_fd, timeout_secs, verbose);
    379     ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b,
    380                                noisy, verbose, &sense_cat);
    381     resid = get_scsi_pt_resid(ptvp);
    382     if (residp)
    383         *residp = resid;
    384     if (-1 == ret)
    385         ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
    386     else if (-2 == ret) {
    387         switch (sense_cat) {
    388         case SG_LIB_CAT_RECOVERED:
    389         case SG_LIB_CAT_NO_SENSE:
    390             ret = 0;
    391             break;
    392         default:
    393             ret = sense_cat;
    394             break;
    395         }
    396     } else if (ret < 4) {
    397         if (verbose)
    398             pr2ws("%s: got too few bytes (%d)\n", __func__, ret);
    399         ret = SG_LIB_CAT_MALFORMED;
    400     } else
    401         ret = 0;
    402     destruct_scsi_pt_obj(ptvp);
    403 
    404     if (resid > 0) {
    405         if (resid > mx_resp_len) {
    406             pr2ws("%s resid (%d) should never exceed requested "
    407                     "len=%d\n", inquiry_s, resid, mx_resp_len);
    408             return ret ? ret : SG_LIB_CAT_MALFORMED;
    409         }
    410         /* zero unfilled section of response buffer, based on resid */
    411         memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
    412     }
    413     return ret;
    414 }
    415 
    416 /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
    417  * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
    418  * The CMDDT field is obsolete in the INQUIRY cdb. */
    419 int
    420 sg_ll_inquiry(int sg_fd, bool cmddt, bool evpd, int pg_op, void * resp,
    421               int mx_resp_len, bool noisy, int verbose)
    422 {
    423     return sg_ll_inquiry_com(sg_fd, cmddt, evpd, pg_op, resp, mx_resp_len,
    424                              0 /* timeout_sec */, NULL, noisy, verbose);
    425 }
    426 
    427 /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
    428  * Returns 0 when successful, various SG_LIB_CAT_* positive values or
    429  * -1 -> other errors */
    430 int
    431 sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
    432                   bool noisy, int verbose)
    433 {
    434     int ret;
    435     unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
    436 
    437     if (inq_data) {
    438         memset(inq_data, 0, sizeof(* inq_data));
    439         inq_data->peripheral_qualifier = 0x3;
    440         inq_data->peripheral_type = 0x1f;
    441     }
    442     ret = sg_ll_inquiry_com(sg_fd, false, false, 0, inq_resp,
    443                             sizeof(inq_resp), 0, NULL, noisy, verbose);
    444 
    445     if (inq_data && (0 == ret)) {
    446         inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
    447         inq_data->peripheral_type = inq_resp[0] & 0x1f;
    448         inq_data->byte_1 = inq_resp[1];
    449         inq_data->version = inq_resp[2];
    450         inq_data->byte_3 = inq_resp[3];
    451         inq_data->byte_5 = inq_resp[5];
    452         inq_data->byte_6 = inq_resp[6];
    453         inq_data->byte_7 = inq_resp[7];
    454         memcpy(inq_data->vendor, inq_resp + 8, 8);
    455         memcpy(inq_data->product, inq_resp + 16, 16);
    456         memcpy(inq_data->revision, inq_resp + 32, 4);
    457     }
    458     return ret;
    459 }
    460 
    461 /* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
    462  * successful, various SG_LIB_CAT_* positive values or -1 -> other errors.
    463  * The CMDDT field is obsolete in the INQUIRY cdb (since spc3r16 in 2003) so
    464  * an argument to set it has been removed (use the REPORT SUPPORTED OPERATION
    465  * CODES command instead). Adds the ability to set the command abort timeout
    466  * and the ability to report the residual count. If timeout_secs is zero
    467  * or less the default command abort timeout (60 seconds) is used.
    468  * If residp is non-NULL then the residual value is written where residp
    469  * points. A residual value of 0 implies mx_resp_len bytes have be written
    470  * where resp points. If the residual value equals mx_resp_len then no
    471  * bytes have been written. */
    472 int
    473 sg_ll_inquiry_v2(int sg_fd, bool evpd, int pg_op, void * resp,
    474                  int mx_resp_len, int timeout_secs, int * residp,
    475                  bool noisy, int verbose)
    476 {
    477     return sg_ll_inquiry_com(sg_fd, false, evpd, pg_op, resp, mx_resp_len,
    478                              timeout_secs, residp, noisy, verbose);
    479 }
    480 
    481 /* Invokes a SCSI TEST UNIT READY command.
    482  * 'pack_id' is just for diagnostics, safe to set to 0.
    483  * Looks for progress indicator if 'progress' non-NULL;
    484  * if found writes value [0..65535] else write -1.
    485  * Returns 0 when successful, various SG_LIB_CAT_* positive values or
    486  * -1 -> other errors */
    487 int
    488 sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
    489                                bool noisy, int verbose)
    490 {
    491     static const char * const tur_s = "test unit ready";
    492     int res, ret, k, sense_cat;
    493     unsigned char tur_cdb[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
    494     unsigned char sense_b[SENSE_BUFF_LEN];
    495     struct sg_pt_base * ptvp;
    496 
    497     if (verbose) {
    498         pr2ws("    %s cdb: ", tur_s);
    499         for (k = 0; k < TUR_CMDLEN; ++k)
    500             pr2ws("%02x ", tur_cdb[k]);
    501         pr2ws("\n");
    502     }
    503 
    504     if (NULL == ((ptvp = create_pt_obj(tur_s))))
    505         return -1;
    506     set_scsi_pt_cdb(ptvp, tur_cdb, sizeof(tur_cdb));
    507     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
    508     set_scsi_pt_packet_id(ptvp, pack_id);
    509     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
    510     ret = sg_cmds_process_resp(ptvp, tur_s, res, SG_NO_DATA_IN, sense_b,
    511                                noisy, verbose, &sense_cat);
    512     if (-1 == ret) {
    513         int os_err = get_scsi_pt_os_err(ptvp);
    514 
    515         if ((os_err > 0) && (os_err < 47))
    516             ret = SG_LIB_OS_BASE_ERR + os_err;
    517     } else if (-2 == ret) {
    518         if (progress) {
    519             int slen = get_scsi_pt_sense_len(ptvp);
    520 
    521             if (! sg_get_sense_progress_fld(sense_b, slen, progress))
    522                 *progress = -1;
    523         }
    524         switch (sense_cat) {
    525         case SG_LIB_CAT_RECOVERED:
    526         case SG_LIB_CAT_NO_SENSE:
    527             ret = 0;
    528             break;
    529         default:
    530             ret = sense_cat;
    531             break;
    532         }
    533     } else
    534         ret = 0;
    535 
    536     destruct_scsi_pt_obj(ptvp);
    537     return ret;
    538 }
    539 
    540 /* Invokes a SCSI TEST UNIT READY command.
    541  * 'pack_id' is just for diagnostics, safe to set to 0.
    542  * Returns 0 when successful, various SG_LIB_CAT_* positive values or
    543  * -1 -> other errors */
    544 int
    545 sg_ll_test_unit_ready(int sg_fd, int pack_id, bool noisy, int verbose)
    546 {
    547     return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
    548                                           verbose);
    549 }
    550 
    551 /* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
    552  * SG_LIB_CAT_* positive values or -1 -> other errors */
    553 int
    554 sg_ll_request_sense(int sg_fd, bool desc, void * resp, int mx_resp_len,
    555                     bool noisy, int verbose)
    556 {
    557     static const char * const rq_s = "request sense";
    558     int k, ret, res, sense_cat;
    559     unsigned char rs_cdb[REQUEST_SENSE_CMDLEN] =
    560         {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
    561     unsigned char sense_b[SENSE_BUFF_LEN];
    562     struct sg_pt_base * ptvp;
    563 
    564     if (desc)
    565         rs_cdb[1] |= 0x1;
    566     if (mx_resp_len > 0xff) {
    567         pr2ws("mx_resp_len cannot exceed 255\n");
    568         return -1;
    569     }
    570     rs_cdb[4] = mx_resp_len & 0xff;
    571     if (verbose) {
    572         pr2ws("    %s cmd: ", rq_s);
    573         for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
    574             pr2ws("%02x ", rs_cdb[k]);
    575         pr2ws("\n");
    576     }
    577 
    578     if (NULL == ((ptvp = create_pt_obj(rq_s))))
    579         return -1;
    580     set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
    581     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
    582     set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
    583     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
    584     ret = sg_cmds_process_resp(ptvp, rq_s, res, mx_resp_len, sense_b, noisy,
    585                                verbose, &sense_cat);
    586     if (-1 == ret) {
    587         int os_err = get_scsi_pt_os_err(ptvp);
    588 
    589         if ((os_err > 0) && (os_err < 47))
    590             ret = SG_LIB_OS_BASE_ERR + os_err;
    591     } else if (-2 == ret) {
    592         switch (sense_cat) {
    593         case SG_LIB_CAT_RECOVERED:
    594         case SG_LIB_CAT_NO_SENSE:
    595             ret = 0;
    596             break;
    597         default:
    598             ret = sense_cat;
    599             break;
    600         }
    601     } else {
    602         if ((mx_resp_len >= 8) && (ret < 8)) {
    603             if (verbose)
    604                 pr2ws("    %s: got %d bytes in response, too short\n", rq_s,
    605                       ret);
    606             ret = -1;
    607         } else
    608             ret = 0;
    609     }
    610     destruct_scsi_pt_obj(ptvp);
    611     return ret;
    612 }
    613 
    614 /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
    615  * various SG_LIB_CAT_* positive values or -1 -> other errors */
    616 int
    617 sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
    618                   bool noisy, int verbose)
    619 {
    620     static const char * const report_luns_s = "report luns";
    621     int k, ret, res, sense_cat;
    622     unsigned char rl_cdb[REPORT_LUNS_CMDLEN] =
    623                          {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    624     unsigned char sense_b[SENSE_BUFF_LEN];
    625     struct sg_pt_base * ptvp;
    626 
    627     rl_cdb[2] = select_report & 0xff;
    628     sg_put_unaligned_be32((uint32_t)mx_resp_len, rl_cdb + 6);
    629     if (verbose) {
    630         pr2ws("    %s cdb: ", report_luns_s);
    631         for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
    632             pr2ws("%02x ", rl_cdb[k]);
    633         pr2ws("\n");
    634     }
    635 
    636     if (NULL == ((ptvp = create_pt_obj(report_luns_s))))
    637         return -1;
    638     set_scsi_pt_cdb(ptvp, rl_cdb, sizeof(rl_cdb));
    639     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
    640     set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
    641     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
    642     ret = sg_cmds_process_resp(ptvp, report_luns_s, res, mx_resp_len,
    643                                sense_b, noisy, verbose, &sense_cat);
    644     if (-1 == ret) {
    645         int os_err = get_scsi_pt_os_err(ptvp);
    646 
    647         if ((os_err > 0) && (os_err < 47))
    648             ret = SG_LIB_OS_BASE_ERR + os_err;
    649     } else if (-2 == ret) {
    650         switch (sense_cat) {
    651         case SG_LIB_CAT_RECOVERED:
    652         case SG_LIB_CAT_NO_SENSE:
    653             ret = 0;
    654             break;
    655         default:
    656             ret = sense_cat;
    657             break;
    658         }
    659     } else
    660         ret = 0;
    661     destruct_scsi_pt_obj(ptvp);
    662     return ret;
    663 }
    664