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