sysmaster/backport-feature-add-scsi_id.patch

1613 lines
49 KiB
Diff
Raw Normal View History

2023-12-07 00:19:38 +08:00
From cc238ef5521071f649affa2e029462a14e5af76a Mon Sep 17 00:00:00 2001
From: jcg <jiangchuangang@huawei.com>
Date: Wed, 13 Sep 2023 19:42:22 +0800
Subject: [PATCH 002/103] feature: add scsi_id
---
exts/devmaster/Cargo.toml | 4 +
exts/devmaster/src/bin/tools/scsi_id/main.rs | 1577 ++++++++++++++++++
2 files changed, 1581 insertions(+)
create mode 100644 exts/devmaster/src/bin/tools/scsi_id/main.rs
diff --git a/exts/devmaster/Cargo.toml b/exts/devmaster/Cargo.toml
index edd73ca2..c52e9880 100644
--- a/exts/devmaster/Cargo.toml
+++ b/exts/devmaster/Cargo.toml
@@ -13,6 +13,10 @@ path = "src/bin/devctl/main.rs"
name = "ata_id"
path = "src/bin/tools/ata_id/main.rs"
+[[bin]]
+name = "scsi_id"
+path = "src/bin/tools/scsi_id/main.rs"
+
[lib]
name = "libdevmaster"
path = "src/lib/lib.rs"
diff --git a/exts/devmaster/src/bin/tools/scsi_id/main.rs b/exts/devmaster/src/bin/tools/scsi_id/main.rs
new file mode 100644
index 00000000..8098c8bf
--- /dev/null
+++ b/exts/devmaster/src/bin/tools/scsi_id/main.rs
@@ -0,0 +1,1577 @@
+// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
+//
+// sysMaster is licensed under Mulan PSL v2.
+// You can use this software according to the terms and conditions of the Mulan
+// PSL v2.
+// You may obtain a copy of Mulan PSL v2 at:
+// http://license.coscl.org.cn/MulanPSL2
+// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
+// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+// See the Mulan PSL v2 for more details.
+
+//! scsi_id - retrieve and generate a unique identifier
+//!
+
+use basic::IN_SET;
+use libc::{c_char, c_void, getopt_long, option, EBADF, EINVAL, ENOSYS};
+use libdevmaster::utils::commons::{encode_devnode_name, replace_chars, replace_whitespace};
+use nix::{
+ errno::{self, Errno},
+ fcntl::{open, OFlag},
+ sys::stat::{self, fstat, major, minor, Mode},
+};
+use scsi_generic_rs::{sg_io_hdr, sg_io_v4, SG_IO};
+use std::{
+ ffi::{CStr, CString},
+ fs::File,
+ io::{BufRead, BufReader},
+ os::{
+ raw::{c_int, c_uchar, c_uint, c_ulonglong},
+ unix::prelude::FromRawFd,
+ },
+ process::exit,
+ ptr::NonNull,
+ str::from_utf8,
+ thread,
+ time::Duration,
+};
+
+extern "C" {
+ ///option arg for getopt_long
+ pub static mut optarg: *mut c_char;
+ ///option index for getopt_long
+ pub static mut optind: c_int;
+}
+
+///device in whitelist or blacklist
+pub static mut ALL_GOOD: bool = false;
+///device specified
+pub static mut DEV_SPECIFIED: bool = false;
+///config file for scsi_id
+pub static mut SCSI_ID_CONFIG: &str = "/etc/scsi_id.config";
+///devnode name
+pub static mut MAJ_MIN_DEV: String = String::new();
+///default pagecode
+pub static mut DEFAULT_PAGE_CODE: PageCode = PageCode::PageUnspecified;
+///default SCSI Generic version
+pub static mut DEFAULT_SG_VERSION: u8 = 4;
+///replace whitespace
+pub static mut REFORMAT_SERIAL: bool = false;
+///print values
+pub static mut EXPORT: bool = false;
+///vendor name
+pub static mut VENDOR: String = String::new();
+///encoded vendor name
+pub static mut VENDOR_ENC_STR: String = String::new();
+///model name
+pub static mut MODEL: String = String::new();
+///encoded model name
+pub static mut MODEL_ENC_STR: String = String::new();
+///revision get from ioctl
+pub static mut REVISION: String = String::new();
+///device type get from ioctl
+pub static mut TYPE_STR: String = String::new();
+///argc get from scsi_id.conf
+pub static mut ARGC: i32 = 0;
+///argv get from scsi_id.conf
+pub static mut ARGV: Vec<String> = Vec::new();
+///default timeout for ioctl
+pub const DEF_TIMEOUT: u32 = 5000;
+///length of SENSE_BUFF
+pub const SENSE_BUFF_LEN: u32 = 32;
+///length of SCSI_INQ_BUFF
+pub const SCSI_INQ_BUFF_LEN: u32 = 254;
+///max length for vendor
+pub const VENDOR_LENGTH: usize = 8;
+///max length for model
+pub const MODEL_LENGTH: usize = 16;
+///inquiry cmd for ioctl
+pub const INQUIRY_CMD: c_uchar = 0x12;
+///inquiry cmd length for ioctl
+pub const INQUIRY_CMDLEN: u32 = 6;
+///id type values of id descriptors
+pub const SCSI_ID_VENDOR_SPECIFIC: c_uchar = 0;
+///id type values of id descriptors
+pub const SCSI_ID_T10_VENDOR: c_uchar = 1;
+///id type values of id descriptors
+pub const SCSI_ID_EUI_64: c_uchar = 2;
+///id type values of id descriptors
+pub const SCSI_ID_NAA: c_uchar = 3;
+///id type values of id descriptors
+pub const SCSI_ID_RELPORT: c_uchar = 4;
+///id type values of id descriptors
+pub const SCSI_ID_TGTGROUP: c_uchar = 5;
+///naa type values of id descriptors
+pub const SCSI_ID_NAA_DONT_CARE: c_uchar = 0xff;
+///naa type values of id descriptors
+pub const SCSI_ID_NAA_IEEE_REG: c_uchar = 0x05;
+///naa type values of id descriptors
+pub const SCSI_ID_NAA_IEEE_REG_EXTENDED: c_uchar = 0x06;
+///code set values of id descriptors
+pub const SCSI_ID_BINARY: c_uchar = 1;
+///code set values of id descriptors
+pub const SCSI_ID_ASCII: c_uchar = 2;
+///SCSI status codes
+pub const SCSI_CHECK_CONDITION: c_uchar = 0x02;
+///SCSI status codes
+pub const SCSI_COMMAND_TERMINATED: c_uchar = 0x22;
+///unable to connect before timeout
+pub const DID_NO_CONNECT: u32 = 0x01;
+///bus remain busy until timeout
+pub const DID_BUS_BUSY: u32 = 0x02;
+///timed out for some other reason
+pub const DID_TIME_OUT: u32 = 0x03;
+///transport disrupted and should retry
+pub const DID_TRANSPORT_DISPUPTED: u32 = 0x0e;
+///driver status
+pub const DRIVER_TIMEOUT: u32 = 0x06;
+///sense_buffer has been set
+pub const DRIVER_SENSE: u32 = 0x08;
+///no errors or other information
+pub const SG_ERR_CAT_CLEAN: i32 = 0;
+///interpreted from sense buffer
+pub const SG_ERR_CAT_MEDIA_CHANGED: i32 = 1;
+///interpreted from sense buffer
+pub const SG_ERR_CAT_RESET: i32 = 2;
+///timeout to get sense buffer
+pub const SG_ERR_CAT_TIMEOUT: i32 = 3;
+///successful command after recovered err
+pub const SG_ERR_CAT_RECOVERED: i32 = 4;
+///illegal / unsupported command
+pub const SG_ERR_CAT_NOTSUPPORTED: i32 = 5;
+///illegal / unsupported command
+pub const SG_ERR_CAT_RETRY: i32 = 6;
+///something else in the sense buffer
+pub const SG_ERR_CAT_SENSE: i32 = 98;
+///some other error/warning
+pub const SG_ERR_CAT_OTHER: i32 = 99;
+///subprotocol of io_v4
+pub const BSG_PROTOCOL_SCSI: u32 = 0;
+///subprotocol of io_v4
+pub const BSG_SUB_PROTOCOL_SCSI_CMD: u32 = 0;
+///dxfer direction of io_hdr
+pub const SG_DXFER_FROM_DEV: i32 = -3;
+///sense key
+pub const RECOVERED_ERROR: u64 = 0x01;
+///sense key
+pub const ILLEGAL_REQUEST: u64 = 0x05;
+///sense key
+pub const UNIT_ATTENTION: u64 = 0x06;
+///max length for serial
+pub const MAX_SERIAL_LEN: i32 = 256;
+
+#[allow(missing_docs)]
+#[derive(Debug)]
+pub enum PageCode {
+ Page83PreSpc3 = -0x83,
+ PageUnspecified = 0x00,
+ Page80 = 0x80,
+ Page83 = 0x83,
+}
+
+impl std::fmt::Display for PageCode {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ PageCode::Page83PreSpc3 => write!(f, "-{:#04x}", 0x83),
+ PageCode::PageUnspecified => write!(f, "{:#04x}", 0x00),
+ PageCode::Page80 => write!(f, "{:#04x}", 0x80),
+ PageCode::Page83 => write!(f, "{:#04x}", 0x83),
+ }
+ }
+}
+
+#[allow(missing_docs)]
+#[derive(Default, Debug)]
+pub struct ScsiIdDevice {
+ vendor: String,
+ model: String,
+ revision: String,
+ kernel: String,
+ serial: String,
+ serial_short: String,
+ r#type: u8,
+ use_sg: u8,
+ unit_serial_number: String,
+ wwn: String,
+ wwn_vendor_extension: String,
+ tgpt_group: String,
+}
+
+#[repr(C)]
+#[allow(non_camel_case_types)]
+#[allow(missing_docs)]
+#[derive(Debug, Copy, Clone)]
+pub struct scsi_id_search_values {
+ id_type: c_uchar,
+ naa_type: c_uchar,
+ code_set: c_uchar,
+}
+#[allow(missing_docs)]
+pub static ID_SEARCH_LIST: [scsi_id_search_values; 13] = [
+ scsi_id_search_values {
+ id_type: SCSI_ID_TGTGROUP,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_IEEE_REG_EXTENDED,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_IEEE_REG_EXTENDED,
+ code_set: SCSI_ID_ASCII,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_IEEE_REG,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_IEEE_REG,
+ code_set: SCSI_ID_ASCII,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_NAA,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_ASCII,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_EUI_64,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_EUI_64,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_ASCII,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_T10_VENDOR,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_T10_VENDOR,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_ASCII,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_VENDOR_SPECIFIC,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_BINARY,
+ },
+ scsi_id_search_values {
+ id_type: SCSI_ID_VENDOR_SPECIFIC,
+ naa_type: SCSI_ID_NAA_DONT_CARE,
+ code_set: SCSI_ID_ASCII,
+ },
+];
+
+fn set_options(argc: i32, argv: Vec<String>) -> i32 {
+ let mut args_c: Vec<*mut c_char> = Vec::new();
+ for option in &argv {
+ args_c.push(CString::new(option.clone()).unwrap().into_raw());
+ }
+ let device = CString::new("device").unwrap();
+ let config = CString::new("config").unwrap();
+ let page = CString::new("page").unwrap();
+ let denylisted = CString::new("denylisted").unwrap();
+ let allowlisted = CString::new("allowlisted").unwrap();
+ let blacklisted = CString::new("blacklisted").unwrap();
+ let whitelisted = CString::new("whitelisted").unwrap();
+ let replace_whitespace = CString::new("replace-whitespace").unwrap();
+ let sg_version = CString::new("sg-version").unwrap();
+ let verbose = CString::new("verbose").unwrap();
+ let version = CString::new("version").unwrap();
+ let export = CString::new("export").unwrap();
+ let help = CString::new("help").unwrap();
+
+ let longopts = [
+ option {
+ name: device.as_ptr(),
+ has_arg: 1,
+ flag: std::ptr::null_mut(),
+ val: b'd' as c_int,
+ },
+ option {
+ name: config.as_ptr(),
+ has_arg: 1,
+ flag: std::ptr::null_mut(),
+ val: b'f' as c_int,
+ },
+ option {
+ name: page.as_ptr(),
+ has_arg: 1,
+ flag: std::ptr::null_mut(),
+ val: b'p' as c_int,
+ },
+ option {
+ name: denylisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'b' as c_int,
+ },
+ option {
+ name: allowlisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'g' as c_int,
+ },
+ option {
+ name: blacklisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'b' as c_int,
+ },
+ option {
+ name: whitelisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'g' as c_int,
+ },
+ option {
+ name: replace_whitespace.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'u' as c_int,
+ },
+ option {
+ name: sg_version.as_ptr(),
+ has_arg: 1,
+ flag: std::ptr::null_mut(),
+ val: b's' as c_int,
+ },
+ option {
+ name: verbose.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'v' as c_int,
+ },
+ option {
+ name: version.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'V' as c_int,
+ },
+ option {
+ name: export.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'x' as c_int,
+ },
+ option {
+ name: help.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'h' as c_int,
+ },
+ option {
+ name: std::ptr::null_mut(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: 0,
+ },
+ ];
+ unsafe {
+ optind = 1;
+ let optstring = CString::new("d:f:gp:uvVxhbs:").unwrap();
+
+ loop {
+ let c = getopt_long(
+ argc as c_int,
+ args_c.as_ptr() as *const *mut c_char,
+ optstring.as_ptr(),
+ longopts.as_ptr(),
+ &mut 0,
+ );
+
+ if c == -1 {
+ break;
+ }
+
+ match c as u8 as char {
+ 'b' => ALL_GOOD = false,
+ 'd' => {
+ DEV_SPECIFIED = true;
+ let optarg_tmp = CStr::from_ptr(optarg as *const c_char);
+ MAJ_MIN_DEV = from_utf8(optarg_tmp.to_bytes()).unwrap().to_string();
+ }
+ 'f' => {
+ let optarg_tmp = CStr::from_ptr(optarg as *const c_char);
+ SCSI_ID_CONFIG = optarg_tmp.to_str().unwrap();
+ }
+ 'g' => ALL_GOOD = true,
+ 'h' => {
+ println!("Usage: scsi_id [OPTION...] DEVICE");
+ println!("SCSI device identification.");
+ println!();
+ println!(" -h --help Print this message");
+ println!(" -V --version Print version of the program");
+ println!(" -d --device= Device node for SG_IO commands");
+ println!(" -f --config= Location of config file");
+ println!(
+ " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)"
+ );
+ println!(" -s --sg-version=3|4 Use SGv3 or SGv4");
+ println!(" -b --blacklisted Treat device as blacklist");
+ println!(" -g --whitelisted Treat device as whitelisted");
+ println!(
+ " -u --replace-whitespace Replace all whitespace by underscores"
+ );
+ println!(" -x --export Print values as environment keys");
+ exit(0);
+ }
+ 'p' => {
+ let optarg_tmp = CStr::from_ptr(optarg as *const c_char);
+ let pagecode = optarg_tmp.to_str().unwrap();
+ match pagecode {
+ "0x80" => DEFAULT_PAGE_CODE = PageCode::Page80,
+ "0x83" => DEFAULT_PAGE_CODE = PageCode::Page83,
+ "pre-spc3-83" => DEFAULT_PAGE_CODE = PageCode::Page83PreSpc3,
+ _ => {
+ log::error!("unknown page code.");
+ return -1;
+ }
+ }
+ }
+ 's' => {
+ let optarg_tmp = CStr::from_ptr(optarg as *const c_char);
+ let sg_version_str = optarg_tmp.to_str().unwrap();
+ DEFAULT_SG_VERSION = sg_version_str.parse().unwrap();
+ if !(3..=4).contains(&DEFAULT_SG_VERSION) {
+ return -1;
+ }
+ }
+ 'u' => REFORMAT_SERIAL = true,
+ 'V' => {
+ println!("scsi_id {}", env!("CARGO_PKG_VERSION"));
+ return 0;
+ }
+ 'x' => EXPORT = true,
+ _ => {
+ println!("invalid arguments");
+ return -1;
+ }
+ }
+ }
+
+ if optind < argc && !DEV_SPECIFIED {
+ DEV_SPECIFIED = true;
+ let tmp = CStr::from_ptr(args_c[optind as usize] as *const c_char);
+ MAJ_MIN_DEV = from_utf8(tmp.to_bytes()).unwrap().to_string();
+ }
+ }
+
+ 0
+}
+
+fn sg_err_category_new(
+ mut scsi_status: u32,
+ host_status: u32,
+ driver_status: u32,
+ sense: u64,
+ sb_len: u32,
+) -> i32 {
+ scsi_status &= 0x7e;
+ if scsi_status == 0 && host_status == 0 && driver_status == 0 {
+ return SG_ERR_CAT_CLEAN;
+ }
+
+ if IN_SET!(
+ scsi_status,
+ SCSI_CHECK_CONDITION as u32,
+ SCSI_COMMAND_TERMINATED as u32
+ ) || (driver_status & 0xf) == DRIVER_SENSE
+ {
+ let sense_buffer;
+ let non_null_ptr = NonNull::new(sense as *mut u64);
+ match non_null_ptr {
+ Some(sense) => {
+ sense_buffer = unsafe {
+ std::slice::from_raw_parts(sense.as_ptr() as *mut u64, SENSE_BUFF_LEN as usize)
+ };
+ }
+ None => {
+ return SG_ERR_CAT_SENSE;
+ }
+ }
+ if sb_len > 2 {
+ let sense_key;
+ let asc;
+
+ if (sense_buffer[0] & 0x2) != 0 {
+ sense_key = sense_buffer[1] & 0xf;
+ asc = sense_buffer[2];
+ } else {
+ sense_key = sense_buffer[2] & 0xf;
+ if sb_len > 12 {
+ asc = sense_buffer[12];
+ } else {
+ asc = 0;
+ }
+ }
+
+ if sense_key == RECOVERED_ERROR {
+ return SG_ERR_CAT_RECOVERED;
+ } else if sense_key == UNIT_ATTENTION {
+ if asc == 0x28 {
+ return SG_ERR_CAT_MEDIA_CHANGED;
+ }
+
+ if asc == 0x29 {
+ return SG_ERR_CAT_RESET;
+ }
+ } else if sense_key == ILLEGAL_REQUEST {
+ return SG_ERR_CAT_NOTSUPPORTED;
+ }
+ return SG_ERR_CAT_SENSE;
+ }
+ if host_status != 0 {
+ if IN_SET!(host_status, DID_NO_CONNECT, DID_BUS_BUSY, DID_TIME_OUT) {
+ return SG_ERR_CAT_TIMEOUT;
+ }
+
+ if host_status == DID_TRANSPORT_DISPUPTED {
+ return SG_ERR_CAT_RETRY;
+ }
+ }
+
+ if driver_status != 0 && driver_status == DRIVER_TIMEOUT {
+ return SG_ERR_CAT_TIMEOUT;
+ }
+ }
+ SG_ERR_CAT_OTHER
+}
+
+fn sg_err_catagory4(io_buf_v4: &mut sg_io_v4) -> i32 {
+ sg_err_category_new(
+ io_buf_v4.device_status,
+ io_buf_v4.transport_status,
+ io_buf_v4.device_status,
+ io_buf_v4.response,
+ io_buf_v4.request_len,
+ )
+}
+
+fn sg_err_catagory3(io_buf_hdr: &mut sg_io_hdr) -> i32 {
+ sg_err_category_new(
+ io_buf_hdr.status.into(),
+ io_buf_hdr.host_status.into(),
+ io_buf_hdr.driver_status.into(),
+ io_buf_hdr.sbp as u64,
+ io_buf_hdr.sb_len_wr.into(),
+ )
+}
+
+fn scsi_dump_sense(dev_scsi: &mut ScsiIdDevice, sense: u64, sb_len: u32) -> i32 {
+ if sb_len < 1 {
+ log::debug!("{}: sense buffer empty.", dev_scsi.kernel);
+ return -EINVAL;
+ }
+ let sense_buffer;
+ let non_null_ptr = NonNull::new(sense as *mut u64);
+ match non_null_ptr {
+ Some(sense) => {
+ sense_buffer =
+ unsafe { std::slice::from_raw_parts(sense.as_ptr() as *mut u64, sb_len as usize) };
+ }
+ None => {
+ return -EINVAL;
+ }
+ }
+ let s;
+ let sense_key;
+ let asc;
+ let ascq;
+
+ let sense_class = sense_buffer[0] >> 4 & 0x07;
+ let code = sense_buffer[0] & 0xf;
+
+ if sense_class == 7 {
+ s = sense_buffer[7] as u32 + 8;
+ if sb_len < s {
+ log::debug!(
+ "{}: sense buffer too small {} bytes, {} bytes too short.",
+ dev_scsi.kernel,
+ sb_len,
+ s - sb_len
+ );
+ return -EINVAL;
+ }
+
+ if IN_SET!(code, 0x0, 0x1) {
+ sense_key = sense_buffer[2] & 0xf;
+ if s < 14 {
+ log::debug!("{}: sense result too small {} bytes", dev_scsi.kernel, s);
+ return -EINVAL;
+ }
+ asc = sense_buffer[12];
+ ascq = sense_buffer[13];
+ } else if IN_SET!(code, 0x2, 0x3) {
+ sense_key = sense_buffer[1] & 0xf;
+ asc = sense_buffer[2];
+ ascq = sense_buffer[3];
+ } else {
+ log::debug!("{}: invalid sense code {:x}.", dev_scsi.kernel, code);
+ return -EINVAL;
+ }
+ log::debug!(
+ "{}: sense key {:x} ASC {:x} ASCQ {:x}.",
+ dev_scsi.kernel,
+ sense_key,
+ asc,
+ ascq
+ );
+ } else {
+ if sb_len < 4 {
+ log::debug!(
+ "{}: sense buffer too small {} bytes, {} bytes too short.",
+ dev_scsi.kernel,
+ sb_len,
+ 4 - sb_len
+ );
+ return -EINVAL;
+ }
+
+ if sense_buffer[0] < 15 {
+ log::debug!(
+ "{}: old sense key: {:x}",
+ dev_scsi.kernel,
+ sense_buffer[0] & 0x0f
+ );
+ } else {
+ log::debug!(
+ "{}: sense = {} {}.",
+ dev_scsi.kernel,
+ sense_buffer[0],
+ sense_buffer[2]
+ );
+ }
+ log::debug!(
+ "{}: non-extended sense class {} code {:x}.",
+ dev_scsi.kernel,
+ sense_class,
+ code
+ );
+ }
+
+ -1
+}
+
+fn scsi_dump_v4(dev_scsi: &mut ScsiIdDevice, io_buf_v4: sg_io_v4) -> i32 {
+ if io_buf_v4.device_status != 0
+ && io_buf_v4.transport_status != 0
+ && io_buf_v4.driver_status != 0
+ {
+ log::debug!("scsi_dump_v4: {}: sense buffer empty.", dev_scsi.kernel);
+ return -EINVAL;
+ }
+
+ log::debug!(
+ "scsi_dump_v4: {}: sg_io failed status {:x} {:x} {:x}",
+ dev_scsi.kernel,
+ io_buf_v4.driver_status,
+ io_buf_v4.transport_status,
+ io_buf_v4.device_status
+ );
+ if io_buf_v4.device_status == (SCSI_CHECK_CONDITION as u32) {
+ return scsi_dump_sense(dev_scsi, io_buf_v4.response, io_buf_v4.request_len);
+ }
+
+ -1
+}
+
+fn scsi_dump(dev_scsi: &mut ScsiIdDevice, io_buf_hdr: sg_io_hdr) -> i32 {
+ if io_buf_hdr.status != 0
+ && io_buf_hdr.host_status != 0
+ && io_buf_hdr.msg_status != 0
+ && io_buf_hdr.driver_status != 0
+ {
+ log::debug!("scsi_dump: {}: sense buffer empty.", dev_scsi.kernel);
+ return -EINVAL;
+ }
+
+ log::debug!(
+ "scsi_dump: {}: sg_io failed status {:x} {:x} {:x} {:x}",
+ dev_scsi.kernel,
+ io_buf_hdr.driver_status,
+ io_buf_hdr.host_status,
+ io_buf_hdr.msg_status,
+ io_buf_hdr.status
+ );
+ if io_buf_hdr.status == (SCSI_CHECK_CONDITION as u8) {
+ return scsi_dump_sense(dev_scsi, io_buf_hdr.sbp as u64, io_buf_hdr.sb_len_wr as u32);
+ }
+
+ -1
+}
+
+fn scsi_inquiry(
+ dev_scsi: &mut ScsiIdDevice,
+ fd: i32,
+ evpd: c_uchar,
+ page: c_uchar,
+ buffer: &mut [c_uchar],
+ buflen: u32,
+) -> i32 {
+ let inq_cmd: [c_uchar; INQUIRY_CMDLEN as usize] =
+ [INQUIRY_CMD, evpd, page, 0, buflen as c_uchar, 0];
+ let sense = [0_u8; SENSE_BUFF_LEN as usize];
+ let mut io_buf_v4 = sg_io_v4::default();
+ let mut io_buf_hdr = sg_io_hdr::default();
+ let mut r = 0;
+ let mut retry = 3;
+
+ while retry > 0 {
+ if dev_scsi.use_sg == 4 {
+ io_buf_v4.guard = 'Q' as c_int;
+ io_buf_v4.protocol = BSG_PROTOCOL_SCSI;
+ io_buf_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
+ io_buf_v4.request_len = INQUIRY_CMDLEN;
+ io_buf_v4.request = inq_cmd.as_ptr() as c_ulonglong;
+ io_buf_v4.max_response_len = SENSE_BUFF_LEN;
+ io_buf_v4.response = sense.as_ptr() as c_ulonglong;
+ io_buf_v4.din_xfer_len = buflen;
+ io_buf_v4.din_xferp = buffer.as_mut_ptr() as c_ulonglong;
+
+ r = unsafe { libc::ioctl(fd, (SG_IO as i32).try_into().unwrap(), &io_buf_v4) };
+ if r < 0 {
+ if IN_SET!(errno::errno(), EINVAL, ENOSYS) {
+ dev_scsi.use_sg = 3;
+ } else {
+ log::debug!("{}: ioctl failed for io_buf_v4!", dev_scsi.kernel);
+ return r;
+ }
+ }
+ }
+
+ if dev_scsi.use_sg != 4 {
+ io_buf_hdr.interface_id = 'S' as c_int;
+ io_buf_hdr.cmd_len = INQUIRY_CMDLEN as c_uchar;
+ io_buf_hdr.mx_sb_len = SENSE_BUFF_LEN as c_uchar;
+ io_buf_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_buf_hdr.dxfer_len = buflen;
+ io_buf_hdr.dxferp = buffer.as_mut_ptr() as *mut c_void;
+ io_buf_hdr.cmdp = inq_cmd.as_ptr() as *mut c_uchar;
+ io_buf_hdr.sbp = sense.as_ptr() as *mut c_uchar;
+ io_buf_hdr.timeout = DEF_TIMEOUT;
+
+ r = unsafe { libc::ioctl(fd, (SG_IO as i32).try_into().unwrap(), &io_buf_hdr) };
+ if r < 0 {
+ log::debug!("{}: ioctl failed for io_buf_hdr!", dev_scsi.kernel);
+ return r;
+ }
+ }
+
+ if dev_scsi.use_sg == 4 {
+ r = sg_err_catagory4(&mut io_buf_v4);
+ } else {
+ r = sg_err_catagory3(&mut io_buf_hdr);
+ }
+
+ match r {
+ SG_ERR_CAT_NOTSUPPORTED => {
+ buffer[1] = 0_u8;
+ r = 0;
+ }
+ SG_ERR_CAT_CLEAN | SG_ERR_CAT_RECOVERED => {
+ r = 0;
+ }
+ SG_ERR_CAT_RETRY => {}
+ _ => {
+ if dev_scsi.use_sg == 4 {
+ r = scsi_dump_v4(dev_scsi, io_buf_v4);
+ } else {
+ r = scsi_dump(dev_scsi, io_buf_hdr);
+ }
+ }
+ }
+
+ if r == 0 {
+ r = buflen as i32;
+ break;
+ }
+
+ if r > 0 && retry == 1 {
+ r = -1
+ }
+
+ retry -= 1;
+ }
+ r
+}
+
+fn scsi_std_inquiry(dev_scsi: &mut ScsiIdDevice, devname: &str) -> i32 {
+ let mut buffer = [0_u8; SCSI_INQ_BUFF_LEN as usize];
+ let fd = match open(
+ devname,
+ OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY,
+ Mode::from_bits(0o666).unwrap(),
+ ) {
+ Ok(fd) => fd,
+ Err(e) => {
+ log::debug!("scsi_std_inquiry: Cannot open {}: {}", devname, e);
+ return e as i32;
+ }
+ };
+
+ let statbuf = match fstat(fd) {
+ Err(_) => {
+ log::debug!("scsi_std_inquiry: can't fstat {}", devname);
+ return 2;
+ }
+ Ok(stat) => stat,
+ };
+
+ dev_scsi.kernel = format!("{}:{}", major(statbuf.st_rdev), minor(statbuf.st_rdev));
+
+ let r = scsi_inquiry(dev_scsi, fd, 0, 0, &mut buffer, SCSI_INQ_BUFF_LEN);
+ if r < 0 {
+ return r;
+ }
+
+ let tmp =
+ unsafe { std::slice::from_raw_parts(buffer.as_mut_ptr() as *mut c_uchar, buffer.len()) };
+ dev_scsi.vendor = String::from_utf8(tmp[8..16].to_vec()).unwrap();
+ dev_scsi.model = String::from_utf8(tmp[16..32].to_vec()).unwrap();
+ dev_scsi.revision = String::from_utf8(tmp[32..36].to_vec()).unwrap();
+ dev_scsi.r#type = tmp[0] as u8 & 0x1f;
+
+ 0
+}
+
+fn set_inq_values(dev_scsi: &mut ScsiIdDevice, maj_min_dev: &str) -> i32 {
+ dev_scsi.use_sg = unsafe { DEFAULT_SG_VERSION };
+
+ let r = scsi_std_inquiry(dev_scsi, maj_min_dev);
+ if r != 0 {
+ return r;
+ }
+
+ encode_devnode_name(&dev_scsi.vendor, unsafe { &mut VENDOR_ENC_STR });
+ encode_devnode_name(&dev_scsi.model, unsafe { &mut MODEL_ENC_STR });
+
+ unsafe {
+ VENDOR = replace_whitespace(&dev_scsi.vendor);
+ VENDOR = replace_chars(&VENDOR, "");
+ MODEL = replace_whitespace(&dev_scsi.model);
+ MODEL = replace_chars(&MODEL, "");
+ REVISION = replace_whitespace(&dev_scsi.revision);
+ REVISION = replace_chars(&REVISION, "");
+ };
+
+ unsafe {
+ TYPE_STR = match dev_scsi.r#type {
+ 0 | 0xe => "disk".to_string(),
+ 1 => "tape".to_string(),
+ 4 | 7 | 0xf => "optical".to_string(),
+ 5 => "cd".to_string(),
+ _ => "generic".to_string(),
+ }
+ }
+
+ 0
+}
+
+fn get_file_options(vendor: String, model: String) -> i32 {
+ let r = 0;
+ let mut vendor_in = String::new();
+ let mut model_in = String::new();
+ let mut options_in = String::new();
+ let file = match open(
+ unsafe { SCSI_ID_CONFIG },
+ OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY,
+ stat::Mode::empty(),
+ ) {
+ Ok(fd) => unsafe { File::from_raw_fd(fd) },
+ Err(e) => {
+ return e as i32;
+ }
+ };
+
+ let reader: BufReader<File> = BufReader::new(file);
+ for (lineno, lines) in reader.lines().enumerate() {
+ let line = match lines {
+ Ok(line) => line,
+ Err(e) => {
+ log::debug!("read line err: {}", e.to_string());
+ return -1;
+ }
+ };
+
+ if line.clone().starts_with('#') {
+ continue;
+ }
+
+ if line.clone().trim().is_empty() {
+ continue;
+ }
+
+ let vec: Vec<String> = line.split(',').map(|s| s.to_string()).collect();
+ for v in vec {
+ let pair: Vec<String> = v.split('=').map(|s| s.to_string()).collect();
+ if pair.len() > 2 {
+ log::error!(
+ "failed to parse config file {} line {}",
+ unsafe { SCSI_ID_CONFIG },
+ lineno
+ );
+ return -1;
+ }
+
+ if pair[0].to_lowercase().starts_with("vendor") {
+ vendor_in = pair[1].clone();
+ }
+
+ if pair[0].to_lowercase().starts_with("model") {
+ model_in = pair[1].clone();
+ }
+
+ if pair[0].to_lowercase().starts_with("options") {
+ options_in = pair[1].clone();
+ }
+ }
+
+ if options_in.is_empty() || (vendor_in.is_empty() && !model_in.is_empty()) {
+ log::error!(
+ "failed to parse config file {} line {}",
+ unsafe { SCSI_ID_CONFIG },
+ lineno
+ );
+ return -1;
+ }
+
+ if vendor.is_empty() {
+ if vendor_in.is_empty() {
+ break;
+ }
+ } else if !vendor_in.is_empty()
+ && vendor.starts_with(&vendor_in)
+ && (model_in.is_empty() || model.starts_with(&model_in))
+ {
+ break;
+ }
+ }
+
+ if vendor_in.is_empty() && model_in.is_empty() && options_in.is_empty() {
+ log::error!("can't get any values from {}", unsafe { SCSI_ID_CONFIG });
+ return -1;
+ }
+ unsafe {
+ ARGV = options_in.split(' ').map(|s| s.to_string()).collect();
+ ARGV.insert(0, "".to_owned());
+ ARGC = ARGV.len() as i32
+ };
+ r
+}
+
+fn per_dev_options(dev_scsi: &mut ScsiIdDevice) -> i32 {
+ let newargc: i32 = 0;
+ let newargv: Vec<String> = Vec::new();
+
+ let page = CString::new("page").unwrap();
+ let denylisted = CString::new("denylisted").unwrap();
+ let allowlisted = CString::new("allowlisted").unwrap();
+ let blacklisted = CString::new("blacklisted").unwrap();
+ let whitelisted = CString::new("whitelisted").unwrap();
+
+ let longopts = [
+ option {
+ name: page.as_ptr(),
+ has_arg: 1,
+ flag: std::ptr::null_mut(),
+ val: b'p' as c_int,
+ },
+ option {
+ name: denylisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'b' as c_int,
+ },
+ option {
+ name: allowlisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'g' as c_int,
+ },
+ option {
+ name: blacklisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'b' as c_int,
+ },
+ option {
+ name: whitelisted.as_ptr(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: b'g' as c_int,
+ },
+ option {
+ name: std::ptr::null_mut(),
+ has_arg: 0,
+ flag: std::ptr::null_mut(),
+ val: 0,
+ },
+ ];
+
+ let mut r = get_file_options(dev_scsi.vendor.clone(), dev_scsi.model.clone());
+ unsafe { optind = 1 };
+ let optstring = CString::new("bgp:").unwrap();
+ while r == 0 {
+ unsafe {
+ let o = getopt_long(
+ newargc,
+ newargv.as_ptr() as *const *mut c_char,
+ optstring.as_ptr(),
+ longopts.as_ptr(),
+ &mut 0,
+ );
+ if o == -1 {
+ break;
+ }
+ match o as u8 as char {
+ 'b' => ALL_GOOD = false,
+ 'g' => ALL_GOOD = true,
+ 'p' => {
+ let optarg_tmp = CStr::from_ptr(optarg as *const c_char);
+ let pagecode = optarg_tmp.to_str().unwrap();
+ match pagecode {
+ "0x80" => DEFAULT_PAGE_CODE = PageCode::Page80,
+ "0x83" => DEFAULT_PAGE_CODE = PageCode::Page83,
+ "pre-spc3-83" => DEFAULT_PAGE_CODE = PageCode::Page83PreSpc3,
+ _ => {
+ log::error!("unknown page code.");
+ return -1;
+ }
+ }
+ }
+ _ => {
+ println!("invalid arguments");
+ r = -1;
+ break;
+ }
+ }
+ }
+ }
+ r
+}
+
+fn do_scsi_page0_inquiry(
+ dev_scsi: &mut ScsiIdDevice,
+ fd: i32,
+ buffer: &mut [c_uchar],
+ len: u32,
+) -> bool {
+ let r = scsi_inquiry(dev_scsi, fd, 1, 0x0, buffer, len);
+ if r < 0 {
+ return false;
+ }
+
+ if buffer[1] != 0_u8 {
+ log::debug!("{}: page 0 not available.", dev_scsi.kernel);
+ return false;
+ }
+
+ if (buffer[3] as u32) > len {
+ log::debug!("{}: page 0 buffer too long {}", dev_scsi.kernel, buffer[3]);
+ return false;
+ }
+
+ if (buffer[3] as i32) > (MODEL_LENGTH as i32)
+ && String::from_utf8(buffer[VENDOR_LENGTH..2 * VENDOR_LENGTH].to_vec()).unwrap()
+ != dev_scsi.vendor[VENDOR_LENGTH..]
+ {
+ log::debug!("{}: invalid page0 data", dev_scsi.kernel);
+ return false;
+ }
+
+ true
+}
+
+fn append_vendor_model(dev_scsi: &mut ScsiIdDevice) -> i32 {
+ if dev_scsi.vendor.len() >= VENDOR_LENGTH {
+ dev_scsi.serial += &dev_scsi.vendor[..VENDOR_LENGTH];
+ } else {
+ log::debug!(
+ "append_vendor_model: {} bad vendor string {}",
+ dev_scsi.kernel,
+ dev_scsi.vendor
+ );
+ return -EINVAL;
+ }
+ if dev_scsi.model.len() >= MODEL_LENGTH {
+ dev_scsi.serial += &dev_scsi.model[..MODEL_LENGTH];
+ } else {
+ log::debug!(
+ "append_vendor_model: {} bad model string {}",
+ dev_scsi.kernel,
+ dev_scsi.model
+ );
+ return -EINVAL;
+ }
+ (VENDOR_LENGTH + MODEL_LENGTH).try_into().unwrap()
+}
+
+fn do_scsi_page80_inquiry(
+ dev_scsi: &mut ScsiIdDevice,
+ fd: i32,
+ get_serial: bool,
+ get_unit_serial_number: bool,
+ max_len: i32,
+) -> i32 {
+ let mut buffer = [0_u8; SCSI_INQ_BUFF_LEN as usize];
+ let r = scsi_inquiry(
+ dev_scsi,
+ fd,
+ 1,
+ PageCode::Page80 as c_uchar,
+ &mut buffer,
+ SCSI_INQ_BUFF_LEN,
+ );
+ if r < 0 {
+ return r;
+ }
+
+ if buffer[1] != (PageCode::Page80 as c_uchar) {
+ log::debug!("{}: Invalid page 0x80.", dev_scsi.kernel);
+ return 1;
+ }
+
+ let len = 1 + (VENDOR_LENGTH + MODEL_LENGTH) as i32 + buffer[3] as i32;
+
+ if max_len < len {
+ log::debug!(
+ "{}: length {} too short - need {}.",
+ dev_scsi.kernel,
+ max_len,
+ len
+ );
+ return 1;
+ }
+
+ let len: usize = (buffer[3] + 4) as usize;
+
+ if get_serial {
+ dev_scsi.serial = "S".to_string();
+ if append_vendor_model(dev_scsi) < 0 {
+ return 1;
+ }
+ dev_scsi.serial += &String::from_utf8(buffer[4..len].to_vec()).unwrap();
+ }
+
+ if get_unit_serial_number {
+ dev_scsi.unit_serial_number = String::from_utf8(buffer[4..len].to_vec()).unwrap();
+ } else {
+ dev_scsi.serial_short = String::from_utf8(buffer[4..len].to_vec()).unwrap();
+ }
+
+ 0
+}
+
+fn check_fill_0x83_prespc3(dev_scsi: &mut ScsiIdDevice, page_83: Vec<c_uchar>, len: i32) -> i32 {
+ if dev_scsi.serial.is_empty() {
+ dev_scsi.serial = format!("{:x}", SCSI_ID_NAA);
+ } else {
+ dev_scsi
+ .serial
+ .replace_range(0..=0, &format!("{:x}", SCSI_ID_NAA));
+ }
+
+ for i in 0..page_83[3] as i32 {
+ dev_scsi.serial += &format!("{:x}", (page_83[4 + i as usize] as i32 & 0xf0) >> 4);
+ dev_scsi.serial += &format!("{:x}", page_83[4 + i as usize] as i32 & 0x0f);
+ if dev_scsi.serial.len() >= ((len - 3) as usize) {
+ break;
+ }
+ }
+ dev_scsi.serial_short = dev_scsi.serial.clone();
+ 0
+}
+
+fn check_fill_0x83_id(
+ dev_scsi: &mut ScsiIdDevice,
+ page_83: Vec<c_uchar>,
+ id_search: scsi_id_search_values,
+ max_len: i32,
+) -> i32 {
+ if (page_83[1] & 0x30) == 0x10 {
+ if id_search.id_type != SCSI_ID_TGTGROUP {
+ return 1;
+ }
+ } else if (page_83[1] & 0x30) != 0 {
+ return 1;
+ }
+
+ if (page_83[1] & 0x0f) != id_search.id_type {
+ return 1;
+ }
+
+ if id_search.naa_type != SCSI_ID_NAA_DONT_CARE && id_search.naa_type != (page_83[4] & 0xf0 >> 4)
+ {
+ return 1;
+ }
+
+ if (page_83[0] & 0x0f) != id_search.code_set {
+ return 1;
+ }
+
+ let mut len: i32 = page_83[3] as i32;
+ if (page_83[0] & 0x0f) != SCSI_ID_ASCII {
+ len *= 2;
+ }
+
+ len += 2;
+ if id_search.id_type == SCSI_ID_VENDOR_SPECIFIC {
+ len += (VENDOR_LENGTH + MODEL_LENGTH) as i32;
+ }
+
+ if max_len < len {
+ log::debug!(
+ "{}: length {} too short - need {}",
+ dev_scsi.kernel,
+ max_len,
+ len
+ );
+ return 1;
+ }
+
+ if id_search.id_type == SCSI_ID_TGTGROUP && !dev_scsi.tgpt_group.is_empty() {
+ let group = ((page_83[6] as c_uint) << 8) | page_83[7] as c_uint;
+ dev_scsi.tgpt_group = format!("{:x}", group);
+ return 1;
+ }
+
+ if !dev_scsi.serial.is_empty() {
+ dev_scsi
+ .serial
+ .replace_range(0..=0, &format!("{:x}", id_search.id_type));
+ } else {
+ dev_scsi.serial = format!("{:x}", id_search.id_type);
+ }
+
+ if id_search.id_type == SCSI_ID_VENDOR_SPECIFIC && append_vendor_model(dev_scsi) < 0 {
+ return -1;
+ }
+
+ let mut i = 4; /* offset to the start of the identifier */
+ let s = dev_scsi.serial.len();
+ if (page_83[0] as c_uchar & 0x0f) == SCSI_ID_ASCII {
+ let range = 4..(4 + page_83[3]);
+ dev_scsi.serial +=
+ &String::from_utf8(page_83[range.start as usize..range.end as usize].to_vec()).unwrap();
+ } else {
+ while i < (4 + page_83[3] as i32) {
+ dev_scsi.serial += &format!("{:x}", (page_83[i as usize] as usize & 0xf0) >> 4);
+ dev_scsi.serial += &format!("{:x}", page_83[i as usize] as usize & 0x0f);
+ i += 1;
+ }
+ }
+
+ dev_scsi.serial_short = dev_scsi.serial[s..].to_string();
+
+ if id_search.id_type == SCSI_ID_NAA && dev_scsi.wwn.is_empty() {
+ let len = dev_scsi.serial.len();
+ if len >= (s + 16) {
+ dev_scsi.wwn = dev_scsi.serial[s..s + 16].to_string();
+ } else {
+ dev_scsi.wwn = dev_scsi.serial[s..].to_string();
+ }
+
+ if dev_scsi.wwn_vendor_extension.is_empty() {
+ if len >= (s + 32) {
+ dev_scsi.wwn_vendor_extension = dev_scsi.serial[s + 16..s + 32].to_string();
+ } else if len >= (s + 16) {
+ dev_scsi.wwn_vendor_extension = dev_scsi.serial[s + 16..].to_string();
+ }
+ }
+ }
+
+ 0
+}
+
+fn do_scsi_page83_inquiry(dev_scsi: &mut ScsiIdDevice, fd: i32, len: i32) -> i32 {
+ let mut page_83 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize];
+ do_scsi_page80_inquiry(dev_scsi, fd, false, true, MAX_SERIAL_LEN);
+ let mut r = scsi_inquiry(
+ dev_scsi,
+ fd,
+ 1,
+ PageCode::Page83 as u8,
+ &mut page_83,
+ SCSI_INQ_BUFF_LEN,
+ );
+ if r < 0 {
+ return 1;
+ }
+
+ if page_83[1] != (PageCode::Page83 as c_uchar) {
+ log::debug!("{}: Invalid page 0x83.", dev_scsi.kernel);
+ return 1;
+ }
+
+ if page_83[6] != 0_u8 {
+ return check_fill_0x83_prespc3(dev_scsi, page_83, len);
+ }
+
+ for id in &ID_SEARCH_LIST {
+ let mut i = 4;
+ while i <= (((page_83[2] as c_uint) << 8) + page_83[3] as c_uint + 3) {
+ r = check_fill_0x83_id(
+ dev_scsi,
+ page_83.clone().split_off(i.try_into().unwrap()),
+ *id,
+ len,
+ );
+ if r <= 0 {
+ return r;
+ }
+ i += page_83[i as usize + 3] as c_uint + 4;
+ }
+ }
+
+ 1
+}
+
+fn do_scsi_page83_prespc3_inquiry(dev_scsi: &mut ScsiIdDevice, fd: i32) -> i32 {
+ let mut page_83 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize];
+ let r = scsi_inquiry(
+ dev_scsi,
+ fd,
+ 1,
+ PageCode::Page83 as u8,
+ &mut page_83,
+ SCSI_INQ_BUFF_LEN,
+ );
+ if r < 0 {
+ return 1;
+ }
+
+ if page_83[1] != (PageCode::Page83 as c_uchar) {
+ log::debug!("{}: Invalid page 0x83.", dev_scsi.kernel);
+ return 1;
+ }
+
+ if page_83[6] == 0_u8 {
+ return 2;
+ }
+
+ if dev_scsi.serial.is_empty() {
+ dev_scsi.serial += &format!("{:x}", SCSI_ID_NAA);
+ } else {
+ dev_scsi
+ .serial
+ .replace_range(0..1, &format!("{:x}", SCSI_ID_NAA));
+ }
+
+ let mut i = 4;
+
+ while i < (page_83[3] as i32 + 4) {
+ dev_scsi.serial += &format!("{:x}", (page_83[i as usize] as usize & 0xf0) >> 4);
+ dev_scsi.serial += &format!("{:x}", page_83[i as usize] as usize & 0x0f);
+ i += 1;
+ }
+
+ 0
+}
+
+fn scsi_get_serial(
+ dev_scsi: &mut ScsiIdDevice,
+ devname: &str,
+ page_code: &PageCode,
+ len: i32,
+) -> i32 {
+ dev_scsi.serial = String::new();
+ let mut fd = -EBADF;
+ let mut page0 = vec![0_u8; SCSI_INQ_BUFF_LEN as usize];
+ for _i in 0..=20 {
+ fd = match open(
+ devname,
+ OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC | OFlag::O_NOCTTY,
+ Mode::from_bits(0o666).unwrap(),
+ ) {
+ Ok(fd) => fd,
+ Err(e) => {
+ log::error!("Cannot open {}: {}", devname, e);
+ if e != Errno::EBUSY {
+ break;
+ }
+ -1
+ }
+ };
+
+ if fd >= 0 {
+ break;
+ }
+ thread::sleep(Duration::from_millis(200));
+ }
+
+ if fd < 0 {
+ return 1;
+ }
+
+ match page_code {
+ PageCode::Page80 => {
+ if do_scsi_page80_inquiry(dev_scsi, fd, true, false, len) != 0 {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ PageCode::Page83 => {
+ if do_scsi_page83_inquiry(dev_scsi, fd, len) != 0 {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ PageCode::Page83PreSpc3 => {
+ let r = do_scsi_page83_prespc3_inquiry(dev_scsi, fd);
+ if r != 0 {
+ if r == 2 {
+ if do_scsi_page83_inquiry(dev_scsi, fd, len) != 0 {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 1;
+ }
+ } else {
+ return r;
+ }
+ }
+ PageCode::PageUnspecified => {}
+ }
+
+ if !do_scsi_page0_inquiry(dev_scsi, fd, &mut page0, SCSI_INQ_BUFF_LEN) {
+ return 1;
+ }
+
+ for ind in 4..=(page0[3] as i32 + 3) {
+ if page0[ind as usize] == (PageCode::Page83 as c_uchar)
+ && do_scsi_page83_inquiry(dev_scsi, fd, len) == 0
+ {
+ return 0;
+ }
+ }
+
+ for ind in 4..=(page0[3] as i32 + 3) {
+ if page0[ind as usize] == (PageCode::Page80 as c_uchar)
+ && do_scsi_page80_inquiry(dev_scsi, fd, true, false, len) == 0
+ {
+ return 0;
+ }
+ }
+ 1
+}
+
+fn scsi_id(maj_min_dev: &str) -> i32 {
+ let mut dev_scsi = ScsiIdDevice::default();
+ let page_code = unsafe { &DEFAULT_PAGE_CODE };
+
+ if set_inq_values(&mut dev_scsi, maj_min_dev) < 0 {
+ return 1;
+ }
+
+ per_dev_options(&mut dev_scsi);
+ if !unsafe { ALL_GOOD } {
+ return 1;
+ }
+
+ scsi_get_serial(&mut dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN);
+ unsafe {
+ if EXPORT {
+ println!("ID_SCSI=1");
+ println!("ID_VENDOR={}", &VENDOR);
+ println!("ID_VENDOR_ENC={}", &VENDOR_ENC_STR);
+ println!("ID_MODEL={}", &MODEL);
+ println!("ID_MODEL_ENC={}", &MODEL_ENC_STR);
+ println!("ID_REVISION={}", &REVISION);
+ println!("ID_TYPE={}", &TYPE_STR);
+
+ if !dev_scsi.serial.is_empty() {
+ dev_scsi.serial = replace_whitespace(&dev_scsi.serial);
+ dev_scsi.serial = replace_chars(&dev_scsi.serial, "");
+ println!("ID_SERIAL={}", dev_scsi.serial);
+ dev_scsi.serial_short = replace_whitespace(&dev_scsi.serial_short);
+ dev_scsi.serial_short = replace_chars(&dev_scsi.serial_short, "");
+ println!("ID_SERIAL_SHORT={}", dev_scsi.serial_short);
+ }
+
+ if !dev_scsi.wwn.is_empty() {
+ println!("ID_WWN=0x{}", dev_scsi.wwn);
+ if !dev_scsi.wwn_vendor_extension.is_empty() {
+ println!(
+ "ID_WWN_VENDOR_EXTENSION=0x{}",
+ dev_scsi.wwn_vendor_extension
+ );
+ println!(
+ "ID_WWN_WITH_EXTENSION=0x{}{}",
+ dev_scsi.wwn, dev_scsi.wwn_vendor_extension
+ );
+ } else {
+ println!("ID_WWN_WITH_EXTENSION=0x{}", dev_scsi.wwn);
+ }
+ }
+
+ if !dev_scsi.tgpt_group.is_empty() {
+ println!("ID_TARGET_PORT={}", dev_scsi.tgpt_group);
+ }
+
+ if !dev_scsi.unit_serial_number.is_empty() {
+ println!("ID_SCSI_SERIAL={}", dev_scsi.unit_serial_number);
+ }
+
+ return 0;
+ }
+ }
+
+ if dev_scsi.serial.is_empty() {
+ return 1;
+ }
+
+ if unsafe { REFORMAT_SERIAL } {
+ dev_scsi.serial = replace_whitespace(&dev_scsi.serial);
+ dev_scsi.serial = replace_chars(&dev_scsi.serial, "");
+ println!("{}", dev_scsi.serial);
+ return 0;
+ }
+
+ println!("{}", dev_scsi.serial);
+ 0
+}
+
+fn main() {
+ let args: Vec<String> = std::env::args().collect();
+ let argc = args.len() as i32;
+
+ let r = get_file_options(String::from(""), String::from(""));
+ if r < 0 {
+ exit(1);
+ }
+
+ if r == 0 && set_options(unsafe { ARGC }, unsafe { ARGV.clone() }) < 0 {
+ exit(2);
+ }
+
+ if set_options(argc, args) < 0 {
+ exit(1);
+ }
+
+ if !unsafe { DEV_SPECIFIED } {
+ log::error!("No device specified!");
+ exit(1);
+ }
+
+ let r = scsi_id(unsafe { &MAJ_MIN_DEV });
+ exit(r);
+}
--
2.33.0