pbs tape: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
		| @ -1,20 +1,17 @@ | ||||
| use std::os::unix::prelude::AsRawFd; | ||||
| use std::io::Write; | ||||
| use std::os::unix::prelude::AsRawFd; | ||||
|  | ||||
| use anyhow::{bail, format_err, Error}; | ||||
| use endian_trait::Endian; | ||||
|  | ||||
| use proxmox_io::{ReadExt, WriteExt}; | ||||
|  | ||||
| use crate::sgutils2::{SgRaw, alloc_page_aligned_buffer}; | ||||
| use crate::sgutils2::{alloc_page_aligned_buffer, SgRaw}; | ||||
|  | ||||
| /// Test if drive supports hardware encryption | ||||
| /// | ||||
| /// We search for AES_GCM algorithm with 256bits key. | ||||
| pub fn has_encryption<F: AsRawFd>( | ||||
|     file: &mut F, | ||||
| ) -> bool { | ||||
|  | ||||
| pub fn has_encryption<F: AsRawFd>(file: &mut F) -> bool { | ||||
|     let data = match sg_spin_data_encryption_caps(file) { | ||||
|         Ok(data) => data, | ||||
|         Err(_) => return false, | ||||
| @ -25,11 +22,7 @@ pub fn has_encryption<F: AsRawFd>( | ||||
| /// Set or clear encryption key | ||||
| /// | ||||
| /// We always use mixed mode, | ||||
| pub fn set_encryption<F: AsRawFd>( | ||||
|     file: &mut F, | ||||
|     key: Option<[u8; 32]>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
| pub fn set_encryption<F: AsRawFd>(file: &mut F, key: Option<[u8; 32]>) -> Result<(), Error> { | ||||
|     let data = match sg_spin_data_encryption_caps(file) { | ||||
|         Ok(data) => data, | ||||
|         Err(_) if key.is_none() => { | ||||
| @ -85,7 +78,6 @@ fn sg_spout_set_encryption<F: AsRawFd>( | ||||
|     algorythm_index: u8, | ||||
|     key: Option<[u8; 32]>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let mut sg_raw = SgRaw::new(file, 0)?; | ||||
|  | ||||
|     let mut outbuf_len = std::mem::size_of::<SspSetDataEncryptionPage>(); | ||||
| @ -106,7 +98,11 @@ fn sg_spout_set_encryption<F: AsRawFd>( | ||||
|         algorythm_index, | ||||
|         key_format: 0, | ||||
|         reserved: [0u8; 8], | ||||
|         key_len: if let Some(ref key) = key { key.len() as u16 } else { 0 }, | ||||
|         key_len: if let Some(ref key) = key { | ||||
|             key.len() as u16 | ||||
|         } else { | ||||
|             0 | ||||
|         }, | ||||
|     }; | ||||
|  | ||||
|     let mut writer = &mut outbuf[..]; | ||||
| @ -119,58 +115,72 @@ fn sg_spout_set_encryption<F: AsRawFd>( | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.push(0xB5); // SECURITY PROTOCOL IN (SPOUT) | ||||
|     cmd.push(0x20); // Tape Data Encryption Page | ||||
|     cmd.push(0);cmd.push(0x10); // Set Data Encryption page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0x10); // Set Data Encryption page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|     cmd.extend(&(outbuf_len as u32).to_be_bytes()); // data out len | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|  | ||||
|     sg_raw.do_out_command(&cmd, &outbuf) | ||||
|     sg_raw | ||||
|         .do_out_command(&cmd, &outbuf) | ||||
|         .map_err(|err| format_err!("set data encryption SPOUT(20h[0010h]) failed - {}", err)) | ||||
| } | ||||
|  | ||||
| // Warning: this blocks and fails if there is no media loaded | ||||
| fn sg_spin_data_encryption_status<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|  | ||||
|     let allocation_len: u32 = 8192+4; | ||||
|     let allocation_len: u32 = 8192 + 4; | ||||
|  | ||||
|     let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; | ||||
|  | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.push(0xA2); // SECURITY PROTOCOL IN (SPIN) | ||||
|     cmd.push(0x20); // Tape Data Encryption Page | ||||
|     cmd.push(0);cmd.push(0x20); // Data Encryption Status page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0x20); // Data Encryption Status page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|     cmd.extend(&allocation_len.to_be_bytes()); | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|  | ||||
|     sg_raw.do_command(&cmd) | ||||
|         .map_err(|err| format_err!("read data encryption status SPIN(20h[0020h]) failed - {}", err)) | ||||
|     sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| { | ||||
|             format_err!( | ||||
|                 "read data encryption status SPIN(20h[0020h]) failed - {}", | ||||
|                 err | ||||
|             ) | ||||
|         }) | ||||
|         .map(|v| v.to_vec()) | ||||
| } | ||||
|  | ||||
| // Warning: this blocks and fails if there is no media loaded | ||||
| fn sg_spin_data_encryption_caps<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|  | ||||
|     let allocation_len: u32 = 8192+4; | ||||
|     let allocation_len: u32 = 8192 + 4; | ||||
|  | ||||
|     let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; | ||||
|  | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.push(0xA2); // SECURITY PROTOCOL IN (SPIN) | ||||
|     cmd.push(0x20); // Tape Data Encryption Page | ||||
|     cmd.push(0);cmd.push(0x10); // Data Encryption Capabilities page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0x10); // Data Encryption Capabilities page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|     cmd.extend(&allocation_len.to_be_bytes()); | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|  | ||||
|     sg_raw.do_command(&cmd) | ||||
|         .map_err(|err| format_err!("read data encryption caps SPIN(20h[0010h]) failed - {}", err)) | ||||
|     sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| { | ||||
|             format_err!( | ||||
|                 "read data encryption caps SPIN(20h[0010h]) failed - {}", | ||||
|                 err | ||||
|             ) | ||||
|         }) | ||||
|         .map(|v| v.to_vec()) | ||||
| } | ||||
|  | ||||
| @ -215,7 +225,6 @@ struct SspDataEncryptionAlgorithmDescriptor { | ||||
|  | ||||
| // Returns the algorythm_index for AES-GCM | ||||
| fn decode_spin_data_encryption_caps(data: &[u8]) -> Result<u8, Error> { | ||||
|  | ||||
|     proxmox_lang::try_block!({ | ||||
|         let mut reader = data; | ||||
|         let _page: SspDataEncryptionCapabilityPage = unsafe { reader.read_be_value()? }; | ||||
| @ -223,9 +232,10 @@ fn decode_spin_data_encryption_caps(data: &[u8]) -> Result<u8, Error> { | ||||
|         let mut aes_gcm_index = None; | ||||
|  | ||||
|         loop { | ||||
|             if reader.is_empty() { break; }; | ||||
|             let desc: SspDataEncryptionAlgorithmDescriptor = | ||||
|                 unsafe { reader.read_be_value()? }; | ||||
|             if reader.is_empty() { | ||||
|                 break; | ||||
|             }; | ||||
|             let desc: SspDataEncryptionAlgorithmDescriptor = unsafe { reader.read_be_value()? }; | ||||
|             if desc.descriptor_len != 0x14 { | ||||
|                 bail!("got wrong key descriptor len"); | ||||
|             } | ||||
| @ -245,8 +255,8 @@ fn decode_spin_data_encryption_caps(data: &[u8]) -> Result<u8, Error> { | ||||
|             Some(index) => Ok(index), | ||||
|             None => bail!("drive does not support AES-GCM encryption"), | ||||
|         } | ||||
|     }).map_err(|err: Error| format_err!("decode data encryption caps page failed - {}", err)) | ||||
|  | ||||
|     }) | ||||
|     .map_err(|err: Error| format_err!("decode data encryption caps page failed - {}", err)) | ||||
| } | ||||
|  | ||||
| #[derive(Endian)] | ||||
| @ -266,7 +276,6 @@ struct SspDataEncryptionStatusPage { | ||||
| } | ||||
|  | ||||
| fn decode_spin_data_encryption_status(data: &[u8]) -> Result<DataEncryptionStatus, Error> { | ||||
|  | ||||
|     proxmox_lang::try_block!({ | ||||
|         let mut reader = data; | ||||
|         let page: SspDataEncryptionStatusPage = unsafe { reader.read_be_value()? }; | ||||
| @ -283,11 +292,9 @@ fn decode_spin_data_encryption_status(data: &[u8]) -> Result<DataEncryptionStatu | ||||
|             _ => bail!("unknown encryption mode"), | ||||
|         }; | ||||
|  | ||||
|         let status = DataEncryptionStatus { | ||||
|             mode, | ||||
|         }; | ||||
|         let status = DataEncryptionStatus { mode }; | ||||
|  | ||||
|         Ok(status) | ||||
|  | ||||
|     }).map_err(|err| format_err!("decode data encryption status page failed - {}", err)) | ||||
|     }) | ||||
|     .map_err(|err| format_err!("decode data encryption status page failed - {}", err)) | ||||
| } | ||||
|  | ||||
| @ -17,7 +17,7 @@ use super::TapeAlertFlags; | ||||
| // see IBM SCSI reference: https://www-01.ibm.com/support/docview.wss?uid=ssg1S7003556&aid=1 | ||||
|  | ||||
| #[derive(Endian)] | ||||
| #[repr(C,packed)] | ||||
| #[repr(C, packed)] | ||||
| struct MamAttributeHeader { | ||||
|     id: u16, | ||||
|     flags: u8, | ||||
| @ -30,8 +30,13 @@ enum MamFormat { | ||||
|     DEC, | ||||
| } | ||||
|  | ||||
| static MAM_ATTRIBUTES: &[ (u16, u16, MamFormat, &str) ] = &[ | ||||
|     (0x00_00, 8, MamFormat::DEC, "Remaining Capacity In Partition"), | ||||
| static MAM_ATTRIBUTES: &[(u16, u16, MamFormat, &str)] = &[ | ||||
|     ( | ||||
|         0x00_00, | ||||
|         8, | ||||
|         MamFormat::DEC, | ||||
|         "Remaining Capacity In Partition", | ||||
|     ), | ||||
|     (0x00_01, 8, MamFormat::DEC, "Maximum Capacity In Partition"), | ||||
|     (0x00_02, 8, MamFormat::DEC, "Tapealert Flags"), | ||||
|     (0x00_03, 8, MamFormat::DEC, "Load Count"), | ||||
| @ -40,19 +45,66 @@ static MAM_ATTRIBUTES: &[ (u16, u16, MamFormat, &str) ] = &[ | ||||
|     (0x00_06, 1, MamFormat::BINARY, "Formatted Density Code"), | ||||
|     (0x00_07, 2, MamFormat::DEC, "Initialization Count"), | ||||
|     (0x00_09, 4, MamFormat::BINARY, "Volume Change Reference"), | ||||
|  | ||||
|     (0x02_0A, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Last Load"), | ||||
|     (0x02_0B, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-1"), | ||||
|     (0x02_0C, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-2"), | ||||
|     (0x02_0D, 40, MamFormat::ASCII, "Device Vendor/Serial Number at Load-3"), | ||||
|  | ||||
|     (0x02_20, 8, MamFormat::DEC, "Total MBytes Written in Medium Life"), | ||||
|     (0x02_21, 8, MamFormat::DEC, "Total MBytes Read In Medium Life"), | ||||
|     (0x02_22, 8, MamFormat::DEC, "Total MBytes Written in Current Load"), | ||||
|     (0x02_23, 8, MamFormat::DEC, "Total MBytes Read in Current/Last Load"), | ||||
|     (0x02_24, 8, MamFormat::BINARY, "Logical Position of First Encrypted Block"), | ||||
|     (0x02_25, 8, MamFormat::BINARY, "Logical Position of First Unencrypted Block After the First Encrypted Block"), | ||||
|  | ||||
|     ( | ||||
|         0x02_0A, | ||||
|         40, | ||||
|         MamFormat::ASCII, | ||||
|         "Device Vendor/Serial Number at Last Load", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_0B, | ||||
|         40, | ||||
|         MamFormat::ASCII, | ||||
|         "Device Vendor/Serial Number at Load-1", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_0C, | ||||
|         40, | ||||
|         MamFormat::ASCII, | ||||
|         "Device Vendor/Serial Number at Load-2", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_0D, | ||||
|         40, | ||||
|         MamFormat::ASCII, | ||||
|         "Device Vendor/Serial Number at Load-3", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_20, | ||||
|         8, | ||||
|         MamFormat::DEC, | ||||
|         "Total MBytes Written in Medium Life", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_21, | ||||
|         8, | ||||
|         MamFormat::DEC, | ||||
|         "Total MBytes Read In Medium Life", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_22, | ||||
|         8, | ||||
|         MamFormat::DEC, | ||||
|         "Total MBytes Written in Current Load", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_23, | ||||
|         8, | ||||
|         MamFormat::DEC, | ||||
|         "Total MBytes Read in Current/Last Load", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_24, | ||||
|         8, | ||||
|         MamFormat::BINARY, | ||||
|         "Logical Position of First Encrypted Block", | ||||
|     ), | ||||
|     ( | ||||
|         0x02_25, | ||||
|         8, | ||||
|         MamFormat::BINARY, | ||||
|         "Logical Position of First Unencrypted Block After the First Encrypted Block", | ||||
|     ), | ||||
|     (0x04_00, 8, MamFormat::ASCII, "Medium Manufacturer"), | ||||
|     (0x04_01, 32, MamFormat::ASCII, "Medium Serial Number"), | ||||
|     (0x04_02, 4, MamFormat::DEC, "Medium Length"), | ||||
| @ -64,27 +116,54 @@ static MAM_ATTRIBUTES: &[ (u16, u16, MamFormat, &str) ] = &[ | ||||
|     (0x04_08, 1, MamFormat::BINARY, "Medium Type"), | ||||
|     (0x04_09, 2, MamFormat::BINARY, "Medium Type Information"), | ||||
|     (0x04_0B, 10, MamFormat::BINARY, "Supported Density Codes"), | ||||
|  | ||||
|     (0x08_00, 8, MamFormat::ASCII, "Application Vendor"), | ||||
|     (0x08_01, 32, MamFormat::ASCII, "Application Name"), | ||||
|     (0x08_02, 8, MamFormat::ASCII, "Application Version"), | ||||
|     (0x08_03, 160, MamFormat::ASCII, "User Medium Text Label"), | ||||
|     (0x08_04, 12, MamFormat::ASCII, "Date And Time Last Written"), | ||||
|     (0x08_05, 1, MamFormat::BINARY, "Text Localization Identifier"), | ||||
|     ( | ||||
|         0x08_05, | ||||
|         1, | ||||
|         MamFormat::BINARY, | ||||
|         "Text Localization Identifier", | ||||
|     ), | ||||
|     (0x08_06, 32, MamFormat::ASCII, "Barcode"), | ||||
|     (0x08_07, 80, MamFormat::ASCII, "Owning Host Textual Name"), | ||||
|     (0x08_08, 160, MamFormat::ASCII, "Media Pool"), | ||||
|     (0x08_0B, 16, MamFormat::ASCII, "Application Format Version"), | ||||
|     (0x08_0C, 50, MamFormat::ASCII, "Volume Coherency Information"), | ||||
|     (0x08_20, 36, MamFormat::ASCII, "Medium Globally Unique Identifier"), | ||||
|     (0x08_21, 36, MamFormat::ASCII, "Media Pool Globally Unique Identifier"), | ||||
|  | ||||
|     (0x10_00, 28,  MamFormat::BINARY, "Unique Cartridge Identify (UCI)"), | ||||
|     (0x10_01, 24,  MamFormat::BINARY, "Alternate Unique Cartridge Identify (Alt-UCI)"), | ||||
|  | ||||
|     ( | ||||
|         0x08_0C, | ||||
|         50, | ||||
|         MamFormat::ASCII, | ||||
|         "Volume Coherency Information", | ||||
|     ), | ||||
|     ( | ||||
|         0x08_20, | ||||
|         36, | ||||
|         MamFormat::ASCII, | ||||
|         "Medium Globally Unique Identifier", | ||||
|     ), | ||||
|     ( | ||||
|         0x08_21, | ||||
|         36, | ||||
|         MamFormat::ASCII, | ||||
|         "Media Pool Globally Unique Identifier", | ||||
|     ), | ||||
|     ( | ||||
|         0x10_00, | ||||
|         28, | ||||
|         MamFormat::BINARY, | ||||
|         "Unique Cartridge Identify (UCI)", | ||||
|     ), | ||||
|     ( | ||||
|         0x10_01, | ||||
|         24, | ||||
|         MamFormat::BINARY, | ||||
|         "Alternate Unique Cartridge Identify (Alt-UCI)", | ||||
|     ), | ||||
| ]; | ||||
|  | ||||
| lazy_static::lazy_static!{ | ||||
| lazy_static::lazy_static! { | ||||
|  | ||||
|     static ref MAM_ATTRIBUTE_NAMES: HashMap<u16, &'static (u16, u16, MamFormat, &'static str)> = { | ||||
|         let mut map = HashMap::new(); | ||||
| @ -98,8 +177,7 @@ lazy_static::lazy_static!{ | ||||
| } | ||||
|  | ||||
| fn read_tape_mam<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|  | ||||
|     let alloc_len: u32 = 32*1024; | ||||
|     let alloc_len: u32 = 32 * 1024; | ||||
|     let mut sg_raw = SgRaw::new(file, alloc_len as usize)?; | ||||
|  | ||||
|     let mut cmd = Vec::new(); | ||||
| @ -108,33 +186,35 @@ fn read_tape_mam<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|     cmd.extend(&alloc_len.to_be_bytes()); // alloc len | ||||
|     cmd.extend(&[0u8, 0u8]); | ||||
|  | ||||
|     sg_raw.do_command(&cmd) | ||||
|     sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| format_err!("read cartidge memory failed - {}", err)) | ||||
|         .map(|v| v.to_vec()) | ||||
| } | ||||
|  | ||||
| /// Read Medium auxiliary memory attributes (cartridge memory) using raw SCSI command. | ||||
| pub fn read_mam_attributes<F: AsRawFd>(file: &mut F) -> Result<Vec<MamAttribute>, Error> { | ||||
|  | ||||
|     let data = read_tape_mam(file)?; | ||||
|  | ||||
|     decode_mam_attributes(&data) | ||||
| } | ||||
|  | ||||
| fn decode_mam_attributes(data: &[u8]) -> Result<Vec<MamAttribute>, Error> { | ||||
|  | ||||
|     let mut reader = data; | ||||
|  | ||||
|     let data_len: u32 = unsafe { reader.read_be_value()? }; | ||||
|  | ||||
|     let expected_len = data_len as usize; | ||||
|  | ||||
|  | ||||
|     if reader.len() < expected_len { | ||||
|         bail!("read_mam_attributes: got unexpected data len ({} != {})", reader.len(), expected_len); | ||||
|         bail!( | ||||
|             "read_mam_attributes: got unexpected data len ({} != {})", | ||||
|             reader.len(), | ||||
|             expected_len | ||||
|         ); | ||||
|     } else if reader.len() > expected_len { | ||||
|         // Note: Quantum hh7 returns the allocation_length instead of real data_len | ||||
|         reader = &data[4..expected_len+4]; | ||||
|         reader = &data[4..expected_len + 4]; | ||||
|     } | ||||
|  | ||||
|     let mut list = Vec::new(); | ||||
| @ -143,7 +223,7 @@ fn decode_mam_attributes(data: &[u8]) -> Result<Vec<MamAttribute>, Error> { | ||||
|         if reader.is_empty() { | ||||
|             break; | ||||
|         } | ||||
|         let head: MamAttributeHeader =  unsafe { reader.read_be_value()? }; | ||||
|         let head: MamAttributeHeader = unsafe { reader.read_be_value()? }; | ||||
|         //println!("GOT ID {:04X} {:08b} {}", head.id, head.flags, head.len); | ||||
|  | ||||
|         let head_id = head.id; | ||||
| @ -164,7 +244,8 @@ fn decode_mam_attributes(data: &[u8]) -> Result<Vec<MamAttribute>, Error> { | ||||
|                         } else if info.1 == 4 { | ||||
|                             format!("{}", u32::from_be_bytes(data[0..4].try_into()?)) | ||||
|                         } else if info.1 == 8 { | ||||
|                             if head_id == 2 { // Tape Alert Flags | ||||
|                             if head_id == 2 { | ||||
|                                 // Tape Alert Flags | ||||
|                                 let value = u64::from_be_bytes(data[0..8].try_into()?); | ||||
|                                 let flags = TapeAlertFlags::from_bits_truncate(value); | ||||
|                                 format!("{:?}", flags) | ||||
| @ -174,7 +255,7 @@ fn decode_mam_attributes(data: &[u8]) -> Result<Vec<MamAttribute>, Error> { | ||||
|                         } else { | ||||
|                             unreachable!(); | ||||
|                         } | ||||
|                     }, | ||||
|                     } | ||||
|                     MamFormat::BINARY => hex::encode(&data), | ||||
|                 }; | ||||
|                 list.push(MamAttribute { | ||||
| @ -183,7 +264,10 @@ fn decode_mam_attributes(data: &[u8]) -> Result<Vec<MamAttribute>, Error> { | ||||
|                     value, | ||||
|                 }); | ||||
|             } else { | ||||
|                 eprintln!("read_mam_attributes: got starnge data len for id {:04X}", head_id); | ||||
|                 eprintln!( | ||||
|                     "read_mam_attributes: got starnge data len for id {:04X}", | ||||
|                     head_id | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             // skip unknown IDs | ||||
| @ -201,8 +285,11 @@ pub struct MediaUsageInfo { | ||||
|  | ||||
| /// Extract Media Usage Information from Cartridge Memory | ||||
| pub fn mam_extract_media_usage(mam: &[MamAttribute]) -> Result<MediaUsageInfo, Error> { | ||||
|  | ||||
|    let manufactured: i64 = match mam.iter().find(|v| v.id == 0x04_06).map(|v| v.value.clone()) { | ||||
|     let manufactured: i64 = match mam | ||||
|         .iter() | ||||
|         .find(|v| v.id == 0x04_06) | ||||
|         .map(|v| v.value.clone()) | ||||
|     { | ||||
|         Some(date_str) => { | ||||
|             if date_str.len() != 8 { | ||||
|                 bail!("unable to parse 'Medium Manufacture Date' - wrong length"); | ||||
| @ -222,15 +309,27 @@ pub fn mam_extract_media_usage(mam: &[MamAttribute]) -> Result<MediaUsageInfo, E | ||||
|         None => bail!("unable to read MAM 'Medium Manufacture Date'"), | ||||
|     }; | ||||
|  | ||||
|     let bytes_written: u64 = match mam.iter().find(|v| v.id == 0x02_20).map(|v| v.value.clone()) { | ||||
|         Some(read_str) => read_str.parse::<u64>()? * 1024*1024, | ||||
|     let bytes_written: u64 = match mam | ||||
|         .iter() | ||||
|         .find(|v| v.id == 0x02_20) | ||||
|         .map(|v| v.value.clone()) | ||||
|     { | ||||
|         Some(read_str) => read_str.parse::<u64>()? * 1024 * 1024, | ||||
|         None => bail!("unable to read MAM 'Total MBytes Written In Medium Life'"), | ||||
|     }; | ||||
|  | ||||
|     let bytes_read: u64 = match mam.iter().find(|v| v.id == 0x02_21).map(|v| v.value.clone()) { | ||||
|         Some(read_str) => read_str.parse::<u64>()? * 1024*1024, | ||||
|     let bytes_read: u64 = match mam | ||||
|         .iter() | ||||
|         .find(|v| v.id == 0x02_21) | ||||
|         .map(|v| v.value.clone()) | ||||
|     { | ||||
|         Some(read_str) => read_str.parse::<u64>()? * 1024 * 1024, | ||||
|         None => bail!("unable to read MAM 'Total MBytes Read In Medium Life'"), | ||||
|     }; | ||||
|  | ||||
|     Ok(MediaUsageInfo { manufactured, bytes_written, bytes_read }) | ||||
|     Ok(MediaUsageInfo { | ||||
|         manufactured, | ||||
|         bytes_written, | ||||
|         bytes_read, | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use anyhow::{bail, format_err, Error}; | ||||
| use std::io::Read; | ||||
| use endian_trait::Endian; | ||||
| use std::io::Read; | ||||
| use std::os::unix::io::AsRawFd; | ||||
|  | ||||
| use proxmox_io::ReadExt; | ||||
| @ -26,14 +26,15 @@ struct DesnityDescriptorBlock { | ||||
| // Returns the maximum supported drive density code | ||||
| pub fn report_density<F: AsRawFd>(file: &mut F) -> Result<u8, Error> { | ||||
|     let alloc_len: u16 = 8192; | ||||
|     let mut sg_raw = SgRaw::new(file,  alloc_len as usize)?; | ||||
|     let mut sg_raw = SgRaw::new(file, alloc_len as usize)?; | ||||
|  | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.extend(&[0x44, 0, 0, 0, 0, 0, 0]); // REPORT DENSITY SUPPORT (MEDIA=0) | ||||
|     cmd.extend(&alloc_len.to_be_bytes()); // alloc len | ||||
|     cmd.push(0u8); // control byte | ||||
|  | ||||
|     let data = sg_raw.do_command(&cmd) | ||||
|     let data = sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| format_err!("report density failed - {}", err))?; | ||||
|  | ||||
|     let mut max_density = 0u8; | ||||
| @ -48,13 +49,15 @@ pub fn report_density<F: AsRawFd>(file: &mut F) -> Result<u8, Error> { | ||||
|             bail!("invalid page length {} {}", page_len + 2, data.len()); | ||||
|         } else { | ||||
|             // Note: Quantum hh7 returns the allocation_length instead of real data_len | ||||
|             reader = &data[2..page_len+2]; | ||||
|             reader = &data[2..page_len + 2]; | ||||
|         } | ||||
|         let mut reserved = [0u8; 2]; | ||||
|         reader.read_exact(&mut reserved)?; | ||||
|  | ||||
|         loop { | ||||
|             if reader.is_empty() { break; } | ||||
|             if reader.is_empty() { | ||||
|                 break; | ||||
|             } | ||||
|             let block: DesnityDescriptorBlock = unsafe { reader.read_be_value()? }; | ||||
|             if block.primary_density_code > max_density { | ||||
|                 max_density = block.primary_density_code; | ||||
| @ -62,8 +65,8 @@ pub fn report_density<F: AsRawFd>(file: &mut F) -> Result<u8, Error> { | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|  | ||||
|     }).map_err(|err| format_err!("decode report density failed - {}", err))?; | ||||
|     }) | ||||
|     .map_err(|err| format_err!("decode report density failed - {}", err))?; | ||||
|  | ||||
|     Ok(max_density) | ||||
| } | ||||
|  | ||||
| @ -7,7 +7,7 @@ use proxmox_io::ReadExt; | ||||
|  | ||||
| use crate::sgutils2::SgRaw; | ||||
|  | ||||
| bitflags::bitflags!{ | ||||
| bitflags::bitflags! { | ||||
|  | ||||
|     /// Tape Alert Flags | ||||
|     /// | ||||
| @ -73,16 +73,13 @@ bitflags::bitflags!{ | ||||
| } | ||||
|  | ||||
| /// Read Tape Alert Flags using raw SCSI command. | ||||
| pub fn read_tape_alert_flags<F: AsRawFd>(file: &mut F) ->  Result<TapeAlertFlags, Error> { | ||||
|  | ||||
| pub fn read_tape_alert_flags<F: AsRawFd>(file: &mut F) -> Result<TapeAlertFlags, Error> { | ||||
|     let data = sg_read_tape_alert_flags(file)?; | ||||
|  | ||||
|     decode_tape_alert_flags(&data) | ||||
| } | ||||
|  | ||||
|  | ||||
| fn sg_read_tape_alert_flags<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|  | ||||
|     let mut sg_raw = SgRaw::new(file, 512)?; | ||||
|  | ||||
|     // Note: We cannjot use LP 2Eh TapeAlerts, because that clears flags on read. | ||||
| @ -91,7 +88,7 @@ fn sg_read_tape_alert_flags<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.push(0x4D); // LOG SENSE | ||||
|     cmd.push(0); | ||||
|     cmd.push((1<<6) | 0x12); // Tape Alert Response log page | ||||
|     cmd.push((1 << 6) | 0x12); // Tape Alert Response log page | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
| @ -99,13 +96,13 @@ fn sg_read_tape_alert_flags<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> | ||||
|     cmd.extend(&[2u8, 0u8]); // alloc len | ||||
|     cmd.push(0u8); // control byte | ||||
|  | ||||
|     sg_raw.do_command(&cmd) | ||||
|     sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| format_err!("read tape alert flags failed - {}", err)) | ||||
|         .map(|v| v.to_vec()) | ||||
| } | ||||
|  | ||||
| fn decode_tape_alert_flags(data: &[u8]) -> Result<TapeAlertFlags, Error> { | ||||
|  | ||||
|     proxmox_lang::try_block!({ | ||||
|         if !((data[0] & 0x7f) == 0x12 && data[1] == 0) { | ||||
|             bail!("invalid response"); | ||||
| @ -130,36 +127,36 @@ fn decode_tape_alert_flags(data: &[u8]) -> Result<TapeAlertFlags, Error> { | ||||
|             bail!("invalid parameter length"); | ||||
|         } | ||||
|  | ||||
|         let mut value: u64 =  unsafe { reader.read_be_value()? }; | ||||
|         let mut value: u64 = unsafe { reader.read_be_value()? }; | ||||
|  | ||||
|         // bits are in wrong order, reverse them | ||||
|         value = value.reverse_bits(); | ||||
|  | ||||
|         Ok(TapeAlertFlags::from_bits_truncate(value)) | ||||
|     }).map_err(|err| format_err!("decode tape alert flags failed - {}", err)) | ||||
|     }) | ||||
|     .map_err(|err| format_err!("decode tape alert flags failed - {}", err)) | ||||
| } | ||||
|  | ||||
| const CRITICAL_FLAG_MASK: u64 = | ||||
| TapeAlertFlags::MEDIA.bits() | | ||||
| TapeAlertFlags::WRITE_FAILURE.bits() | | ||||
| TapeAlertFlags::READ_FAILURE.bits() | | ||||
| TapeAlertFlags::WRITE_PROTECT.bits() | | ||||
| TapeAlertFlags::UNRECOVERABLE_SNAPPED_TAPE.bits() | | ||||
| TapeAlertFlags::FORCED_EJECT.bits() | | ||||
| TapeAlertFlags::EXPIRED_CLEANING_MEDIA.bits() | | ||||
| TapeAlertFlags::INVALID_CLEANING_TAPE.bits() | | ||||
| TapeAlertFlags::HARDWARE_A.bits() | | ||||
| TapeAlertFlags::HARDWARE_B.bits() | | ||||
| TapeAlertFlags::EJECT_MEDIA.bits() | | ||||
| TapeAlertFlags::PREDICTIVE_FAILURE.bits() | | ||||
| TapeAlertFlags::LOADER_STRAY_TAPE.bits() | | ||||
| TapeAlertFlags::LOADER_MAGAZINE.bits() | | ||||
| TapeAlertFlags::TAPE_SYSTEM_AREA_WRITE_FAILURE.bits() | | ||||
| TapeAlertFlags::TAPE_SYSTEM_AREA_READ_FAILURE.bits() | | ||||
| TapeAlertFlags::NO_START_OF_DATA.bits() | | ||||
| TapeAlertFlags::LOADING_FAILURE.bits() | | ||||
| TapeAlertFlags::UNRECOVERABLE_UNLOAD_FAILURE.bits() | | ||||
| TapeAlertFlags::AUTOMATION_INTERFACE_FAILURE.bits(); | ||||
| const CRITICAL_FLAG_MASK: u64 = TapeAlertFlags::MEDIA.bits() | ||||
|     | TapeAlertFlags::WRITE_FAILURE.bits() | ||||
|     | TapeAlertFlags::READ_FAILURE.bits() | ||||
|     | TapeAlertFlags::WRITE_PROTECT.bits() | ||||
|     | TapeAlertFlags::UNRECOVERABLE_SNAPPED_TAPE.bits() | ||||
|     | TapeAlertFlags::FORCED_EJECT.bits() | ||||
|     | TapeAlertFlags::EXPIRED_CLEANING_MEDIA.bits() | ||||
|     | TapeAlertFlags::INVALID_CLEANING_TAPE.bits() | ||||
|     | TapeAlertFlags::HARDWARE_A.bits() | ||||
|     | TapeAlertFlags::HARDWARE_B.bits() | ||||
|     | TapeAlertFlags::EJECT_MEDIA.bits() | ||||
|     | TapeAlertFlags::PREDICTIVE_FAILURE.bits() | ||||
|     | TapeAlertFlags::LOADER_STRAY_TAPE.bits() | ||||
|     | TapeAlertFlags::LOADER_MAGAZINE.bits() | ||||
|     | TapeAlertFlags::TAPE_SYSTEM_AREA_WRITE_FAILURE.bits() | ||||
|     | TapeAlertFlags::TAPE_SYSTEM_AREA_READ_FAILURE.bits() | ||||
|     | TapeAlertFlags::NO_START_OF_DATA.bits() | ||||
|     | TapeAlertFlags::LOADING_FAILURE.bits() | ||||
|     | TapeAlertFlags::UNRECOVERABLE_UNLOAD_FAILURE.bits() | ||||
|     | TapeAlertFlags::AUTOMATION_INTERFACE_FAILURE.bits(); | ||||
|  | ||||
| /// Check if tape-alert-flags contains critial errors. | ||||
| pub fn tape_alert_flags_critical(flags: TapeAlertFlags) -> bool { | ||||
| @ -167,8 +164,7 @@ pub fn tape_alert_flags_critical(flags: TapeAlertFlags) -> bool { | ||||
| } | ||||
|  | ||||
| const MEDIA_LIFE_MASK: u64 = | ||||
| TapeAlertFlags::MEDIA_LIFE.bits() | | ||||
| TapeAlertFlags::NEARING_MEDIA_LIFE.bits(); | ||||
|     TapeAlertFlags::MEDIA_LIFE.bits() | TapeAlertFlags::NEARING_MEDIA_LIFE.bits(); | ||||
|  | ||||
| /// Check if tape-alert-flags indicates media-life end | ||||
| pub fn tape_alert_flags_media_life(flags: TapeAlertFlags) -> bool { | ||||
| @ -176,8 +172,7 @@ pub fn tape_alert_flags_media_life(flags: TapeAlertFlags) -> bool { | ||||
| } | ||||
|  | ||||
| const MEDIA_CLEAN_MASK: u64 = | ||||
| TapeAlertFlags::CLEAN_NOW.bits() | | ||||
| TapeAlertFlags::CLEAN_PERIODIC.bits(); | ||||
|     TapeAlertFlags::CLEAN_NOW.bits() | TapeAlertFlags::CLEAN_PERIODIC.bits(); | ||||
|  | ||||
| /// Check if tape-alert-flags indicates media cleaning request | ||||
| pub fn tape_alert_flags_cleaning_request(flags: TapeAlertFlags) -> bool { | ||||
|  | ||||
| @ -16,22 +16,20 @@ use crate::sgutils2::SgRaw; | ||||
| /// | ||||
| /// The Volume Statistics log page is included in Ultrium 5 and later | ||||
| /// drives. | ||||
| pub fn read_volume_statistics<F: AsRawFd>(file: &mut F) ->  Result<Lp17VolumeStatistics, Error> { | ||||
|  | ||||
| pub fn read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Lp17VolumeStatistics, Error> { | ||||
|     let data = sg_read_volume_statistics(file)?; | ||||
|  | ||||
|     decode_volume_statistics(&data) | ||||
| } | ||||
|  | ||||
| fn sg_read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> { | ||||
|  | ||||
|     let alloc_len: u16 = 8192; | ||||
|     let mut sg_raw = SgRaw::new(file, alloc_len as usize)?; | ||||
|  | ||||
|     let mut cmd = Vec::new(); | ||||
|     cmd.push(0x4D); // LOG SENSE | ||||
|     cmd.push(0); | ||||
|     cmd.push((1<<6) | 0x17); // Volume Statistics log page | ||||
|     cmd.push((1 << 6) | 0x17); // Volume Statistics log page | ||||
|     cmd.push(0); // Subpage 0 | ||||
|     cmd.push(0); | ||||
|     cmd.push(0); | ||||
| @ -39,7 +37,8 @@ fn sg_read_volume_statistics<F: AsRawFd>(file: &mut F) -> Result<Vec<u8>, Error> | ||||
|     cmd.extend(&alloc_len.to_be_bytes()); // alloc len | ||||
|     cmd.push(0u8); // control byte | ||||
|  | ||||
|     sg_raw.do_command(&cmd) | ||||
|     sg_raw | ||||
|         .do_command(&cmd) | ||||
|         .map_err(|err| format_err!("read tape volume statistics failed - {}", err)) | ||||
|         .map(|v| v.to_vec()) | ||||
| } | ||||
| @ -53,8 +52,6 @@ struct LpParameterHeader { | ||||
| } | ||||
|  | ||||
| fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> { | ||||
|  | ||||
|  | ||||
|     let read_be_counter = |reader: &mut &[u8], len: u8| { | ||||
|         let len = len as usize; | ||||
|         if len == 0 || len > 8 { | ||||
| @ -86,7 +83,7 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|             bail!("invalid page length"); | ||||
|         } else { | ||||
|             // Note: Quantum hh7 returns the allocation_length instead of real data_len | ||||
|             reader = &data[4..page_len+4]; | ||||
|             reader = &data[4..page_len + 4]; | ||||
|         } | ||||
|  | ||||
|         let mut stat = Lp17VolumeStatistics::default(); | ||||
| @ -101,14 +98,13 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|             match head.parameter_code { | ||||
|                 0x0000 => { | ||||
|                     let value: u64 = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                      if value == 0 { | ||||
|                          bail!("page-valid flag not set"); | ||||
|                     if value == 0 { | ||||
|                         bail!("page-valid flag not set"); | ||||
|                     } | ||||
|                     page_valid = true; | ||||
|                 } | ||||
|                 0x0001 => { | ||||
|                     stat.volume_mounts = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                     stat.volume_mounts = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0002 => { | ||||
|                     stat.volume_datasets_written = | ||||
| @ -131,8 +127,7 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0007 => { | ||||
|                     stat.volume_datasets_read = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                     stat.volume_datasets_read = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0008 => { | ||||
|                     stat.volume_recovered_read_errors = | ||||
| @ -175,12 +170,10 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0014 => { | ||||
|                     stat.medium_mount_time = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                     stat.medium_mount_time = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0015 => { | ||||
|                     stat.medium_ready_time = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                     stat.medium_ready_time = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0016 => { | ||||
|                     stat.total_native_capacity = | ||||
| @ -207,12 +200,11 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|                     } | ||||
|                 } | ||||
|                 0x0101 => { | ||||
|                    stat.beginning_of_medium_passes = | ||||
|                     stat.beginning_of_medium_passes = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 0x0102 => { | ||||
|                    stat.middle_of_tape_passes = | ||||
|                         read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                     stat.middle_of_tape_passes = read_be_counter(&mut reader, head.parameter_len)?; | ||||
|                 } | ||||
|                 _ => { | ||||
|                     reader.read_exact_allocated(head.parameter_len as usize)?; | ||||
| @ -225,6 +217,6 @@ fn decode_volume_statistics(data: &[u8]) -> Result<Lp17VolumeStatistics, Error> | ||||
|         } | ||||
|  | ||||
|         Ok(stat) | ||||
|  | ||||
|     }).map_err(|err| format_err!("decode volume statistics failed - {}", err)) | ||||
|     }) | ||||
|     .map_err(|err| format_err!("decode volume statistics failed - {}", err)) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user