diff --git a/pbs-tape/src/bin/pmt.rs b/pbs-tape/src/bin/pmt.rs index 500c63faa..5f4f60740 100644 --- a/pbs-tape/src/bin/pmt.rs +++ b/pbs-tape/src/bin/pmt.rs @@ -12,54 +12,46 @@ /// - support tape alert flags /// - support volume statistics /// - read cartridge memory - use std::convert::TryInto; use anyhow::{bail, Error}; use serde_json::Value; -use proxmox_schema::{api, ArraySchema, IntegerSchema, Schema, StringSchema}; use proxmox_router::cli::*; use proxmox_router::RpcEnvironment; +use proxmox_schema::{api, ArraySchema, IntegerSchema, Schema, StringSchema}; -use pbs_api_types::{ - LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, LtoTapeDrive, -}; +use pbs_api_types::{LtoTapeDrive, DRIVE_NAME_SCHEMA, LTO_DRIVE_PATH_SCHEMA}; use pbs_config::drive::complete_drive_name; use pbs_tape::{ - sg_tape::SgTape, linux_list_drives::{complete_drive_path, lto_tape_device_list, open_lto_tape_device}, + sg_tape::SgTape, }; -pub const FILE_MARK_COUNT_SCHEMA: Schema = - IntegerSchema::new("File mark count.") +pub const FILE_MARK_COUNT_SCHEMA: Schema = IntegerSchema::new("File mark count.") .minimum(1) .maximum(i32::MAX as isize) .schema(); -pub const FILE_MARK_POSITION_SCHEMA: Schema = - IntegerSchema::new("File mark position (0 is BOT).") +pub const FILE_MARK_POSITION_SCHEMA: Schema = IntegerSchema::new("File mark position (0 is BOT).") .minimum(0) .maximum(i32::MAX as isize) .schema(); -pub const RECORD_COUNT_SCHEMA: Schema = - IntegerSchema::new("Record count.") +pub const RECORD_COUNT_SCHEMA: Schema = IntegerSchema::new("Record count.") .minimum(1) .maximum(i32::MAX as isize) .schema(); -pub const DRIVE_OPTION_SCHEMA: Schema = StringSchema::new( - "Lto Tape Driver Option, either numeric value or option name.") - .schema(); +pub const DRIVE_OPTION_SCHEMA: Schema = + StringSchema::new("Lto Tape Driver Option, either numeric value or option name.").schema(); pub const DRIVE_OPTION_LIST_SCHEMA: Schema = ArraySchema::new("Drive Option List.", &DRIVE_OPTION_SCHEMA) - .min_length(1) - .schema(); + .min_length(1) + .schema(); fn get_tape_handle(param: &Value) -> Result { - if let Some(name) = param["drive"].as_str() { let (config, _digest) = pbs_config::drive::config()?; let drive: LtoTapeDrive = config.lookup("lto", name)?; @@ -88,7 +80,9 @@ fn get_tape_handle(param: &Value) -> Result { let mut drive_names = Vec::new(); for (name, (section_type, _)) in config.sections.iter() { - if section_type != "lto" { continue; } + if section_type != "lto" { + continue; + } drive_names.push(name); } @@ -122,7 +116,6 @@ fn get_tape_handle(param: &Value) -> Result { /// Position the tape at the beginning of the count file (after /// filemark count) fn asf(count: u64, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.locate_file(count)?; @@ -130,7 +123,6 @@ fn asf(count: u64, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -152,7 +144,6 @@ fn asf(count: u64, param: Value) -> Result<(), Error> { /// /// The tape is positioned on the last block of the previous file. fn bsf(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_filemarks(-count.try_into()?)?; @@ -160,7 +151,6 @@ fn bsf(count: usize, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -183,7 +173,6 @@ fn bsf(count: usize, param: Value) -> Result<(), Error> { /// This leaves the tape positioned at the first block of the file /// that is count - 1 files before the current file. fn bsfm(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_filemarks(-count.try_into()?)?; @@ -192,7 +181,6 @@ fn bsfm(count: usize, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -212,7 +200,6 @@ fn bsfm(count: usize, param: Value) -> Result<(), Error> { )] /// Backward space records. fn bsr(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_blocks(-count.try_into()?)?; @@ -220,7 +207,6 @@ fn bsr(count: usize, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -241,7 +227,6 @@ fn bsr(count: usize, param: Value) -> Result<(), Error> { )] /// Read Cartridge Memory fn cartridge_memory(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let mut handle = get_tape_handle(¶m)?; @@ -292,11 +277,11 @@ fn cartridge_memory(param: Value) -> Result<(), Error> { )] /// Read Tape Alert Flags fn tape_alert_flags(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let mut handle = get_tape_handle(¶m)?; - let result = handle.tape_alert_flags() + let result = handle + .tape_alert_flags() .map(|flags| format!("{:?}", flags)); if output_format == "json-pretty" { @@ -337,14 +322,12 @@ fn tape_alert_flags(param: Value) -> Result<(), Error> { )] /// Eject drive media fn eject(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.eject()?; Ok(()) } - #[api( input: { properties: { @@ -361,14 +344,12 @@ fn eject(param: Value) -> Result<(), Error> { )] /// Move to end of media fn eod(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.move_to_eom(false)?; Ok(()) } - #[api( input: { properties: { @@ -391,7 +372,6 @@ fn eod(param: Value) -> Result<(), Error> { )] /// Erase media (from current position) fn erase(fast: Option, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.erase_media(fast.unwrap_or(true))?; @@ -420,7 +400,6 @@ fn erase(fast: Option, param: Value) -> Result<(), Error> { )] /// Format media, single partition fn format(fast: Option, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.format_media(fast.unwrap_or(true))?; @@ -448,7 +427,6 @@ fn format(fast: Option, param: Value) -> Result<(), Error> { /// /// The tape is positioned on the first block of the next file. fn fsf(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_filemarks(count.try_into()?)?; @@ -478,7 +456,6 @@ fn fsf(count: usize, param: Value) -> Result<(), Error> { /// This leaves the tape positioned at the last block of the file that /// is count - 1 files past the current file. fn fsfm(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_filemarks(count.try_into()?)?; @@ -487,7 +464,6 @@ fn fsfm(count: usize, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -507,7 +483,6 @@ fn fsfm(count: usize, param: Value) -> Result<(), Error> { )] /// Forward space records. fn fsr(count: usize, param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.space_blocks(count.try_into()?)?; @@ -515,7 +490,6 @@ fn fsr(count: usize, param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -532,14 +506,12 @@ fn fsr(count: usize, param: Value) -> Result<(), Error> { )] /// Load media fn load(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.load()?; Ok(()) } - #[api( input: { properties: { @@ -556,7 +528,6 @@ fn load(param: Value) -> Result<(), Error> { )] /// Lock the tape drive door fn lock(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.set_medium_removal(false)?; @@ -564,7 +535,6 @@ fn lock(param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -581,14 +551,12 @@ fn lock(param: Value) -> Result<(), Error> { )] /// Rewind the tape fn rewind(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.rewind()?; Ok(()) } - #[api( input: { properties: { @@ -601,7 +569,6 @@ fn rewind(param: Value) -> Result<(), Error> { )] /// Scan for existing tape changer devices fn scan(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let list = lto_tape_device_list(); @@ -621,7 +588,10 @@ fn scan(param: Value) -> Result<(), Error> { } for item in list.iter() { - println!("{} ({}/{}/{})", item.path, item.vendor, item.model, item.serial); + println!( + "{} ({}/{}/{})", + item.path, item.vendor, item.model, item.serial + ); } Ok(()) @@ -647,7 +617,6 @@ fn scan(param: Value) -> Result<(), Error> { )] /// Drive Status fn status(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let mut handle = get_tape_handle(¶m)?; @@ -677,7 +646,6 @@ fn status(param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -694,7 +662,6 @@ fn status(param: Value) -> Result<(), Error> { )] /// Unlock the tape drive door fn unlock(param: Value) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; handle.set_medium_removal(true)?; @@ -702,7 +669,6 @@ fn unlock(param: Value) -> Result<(), Error> { Ok(()) } - #[api( input: { properties: { @@ -723,7 +689,6 @@ fn unlock(param: Value) -> Result<(), Error> { )] /// Volume Statistics fn volume_statistics(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let mut handle = get_tape_handle(¶m)?; @@ -772,7 +737,6 @@ fn volume_statistics(param: Value) -> Result<(), Error> { )] /// Write count (default 1) EOF marks at current position. fn weof(count: Option, param: Value) -> Result<(), Error> { - let count = count.unwrap_or(1); let mut handle = get_tape_handle(¶m)?; @@ -825,7 +789,6 @@ fn options( defaults: Option, param: Value, ) -> Result<(), Error> { - let mut handle = get_tape_handle(¶m)?; if let Some(true) = defaults { @@ -838,7 +801,6 @@ fn options( } fn main() -> Result<(), Error> { - let uid = nix::unistd::Uid::current(); let username = match nix::unistd::User::from_uid(uid)? { @@ -875,8 +837,7 @@ fn main() -> Result<(), Error> { .insert("tape-alert-flags", std_cmd(&API_METHOD_TAPE_ALERT_FLAGS)) .insert("unlock", std_cmd(&API_METHOD_UNLOCK)) .insert("volume-statistics", std_cmd(&API_METHOD_VOLUME_STATISTICS)) - .insert("weof", std_cmd(&API_METHOD_WEOF).arg_param(&["count"])) - ; + .insert("weof", std_cmd(&API_METHOD_WEOF).arg_param(&["count"])); let mut rpcenv = CliEnvironment::new(); rpcenv.set_auth_id(Some(format!("{}@pam", username))); diff --git a/pbs-tape/src/bin/pmtx.rs b/pbs-tape/src/bin/pmtx.rs index 23dbc0ae7..cea62c74a 100644 --- a/pbs-tape/src/bin/pmtx.rs +++ b/pbs-tape/src/bin/pmtx.rs @@ -11,29 +11,25 @@ /// /// - list serial number for attached drives, so that it is possible /// to associate drive numbers with drives. - use std::fs::File; use anyhow::{bail, Error}; use serde_json::Value; -use proxmox_schema::api; use proxmox_router::cli::*; use proxmox_router::RpcEnvironment; +use proxmox_schema::api; +use pbs_api_types::{LtoTapeDrive, ScsiTapeChanger, CHANGER_NAME_SCHEMA, SCSI_CHANGER_PATH_SCHEMA}; use pbs_config::drive::complete_changer_name; -use pbs_api_types::{ - SCSI_CHANGER_PATH_SCHEMA, CHANGER_NAME_SCHEMA, ScsiTapeChanger, LtoTapeDrive, -}; use pbs_tape::{ + linux_list_drives::{complete_changer_path, linux_tape_changer_list}, + sg_pt_changer, sgutils2::scsi_inquiry, ElementStatus, - sg_pt_changer, - linux_list_drives::{complete_changer_path, linux_tape_changer_list}, }; fn get_changer_handle(param: &Value) -> Result { - if let Some(name) = param["changer"].as_str() { let (config, _digest) = pbs_config::drive::config()?; let changer_config: ScsiTapeChanger = config.lookup("changer", name)?; @@ -83,10 +79,7 @@ fn get_changer_handle(param: &Value) -> Result { }, )] /// Inquiry -fn inquiry( - param: Value, -) -> Result<(), Error> { - +fn inquiry(param: Value) -> Result<(), Error> { let output_format = get_output_format(¶m); let result: Result<_, Error> = proxmox_lang::try_block!({ @@ -113,7 +106,10 @@ fn inquiry( let info = result?; - println!("Type: {} ({})", info.peripheral_type_text, info.peripheral_type); + println!( + "Type: {} ({})", + info.peripheral_type_text, info.peripheral_type + ); println!("Vendor: {}", info.vendor); println!("Product: {}", info.product); println!("Revision: {}", info.revision); @@ -136,10 +132,7 @@ fn inquiry( }, )] /// Inventory -fn inventory( - param: Value, -) -> Result<(), Error> { - +fn inventory(param: Value) -> Result<(), Error> { let mut file = get_changer_handle(¶m)?; sg_pt_changer::initialize_element_status(&mut file)?; @@ -170,12 +163,7 @@ fn inventory( }, )] /// Load -fn load( - param: Value, - slot: u64, - drivenum: Option, -) -> Result<(), Error> { - +fn load(param: Value, slot: u64, drivenum: Option) -> Result<(), Error> { let mut file = get_changer_handle(¶m)?; let drivenum = drivenum.unwrap_or(0); @@ -210,12 +198,7 @@ fn load( }, )] /// Unload -fn unload( - param: Value, - slot: Option, - drivenum: Option, -) -> Result<(), Error> { - +fn unload(param: Value, slot: Option, drivenum: Option) -> Result<(), Error> { let mut file = get_changer_handle(¶m)?; let drivenum = drivenum.unwrap_or(0); @@ -271,10 +254,7 @@ fn unload( }, )] /// Changer Status -fn status( - param: Value, -) -> Result<(), Error> { - +fn status(param: Value) -> Result<(), Error> { let output_format = get_output_format(¶m); let result: Result<_, Error> = proxmox_lang::try_block!({ @@ -302,7 +282,10 @@ fn status( let status = result?; for (i, transport) in status.transports.iter().enumerate() { - println!("Transport Element (Griper) {:>3}: {:?}",i, transport.status); + println!( + "Transport Element (Griper) {:>3}: {:?}", + i, transport.status + ); } for (i, drive) in status.drives.iter().enumerate() { @@ -312,7 +295,7 @@ fn status( }; let serial_txt = match drive.drive_serial_number { Some(ref serial) => format!(", Serial: {}", serial), - None => String::new(), + None => String::new(), }; println!( @@ -323,9 +306,9 @@ fn status( for (i, slot) in status.slots.iter().enumerate() { if slot.import_export { - println!(" Import/Export {:>3}: {:?}", i+1, slot.status); + println!(" Import/Export {:>3}: {:?}", i + 1, slot.status); } else { - println!(" Storage Element {:>3}: {:?}", i+1, slot.status); + println!(" Storage Element {:>3}: {:?}", i + 1, slot.status); } } @@ -355,12 +338,7 @@ fn status( }, )] /// Transfer -fn transfer( - param: Value, - from: u64, - to: u64, -) -> Result<(), Error> { - +fn transfer(param: Value, from: u64, to: u64) -> Result<(), Error> { let mut file = get_changer_handle(¶m)?; sg_pt_changer::transfer_medium(&mut file, from, to)?; @@ -380,7 +358,6 @@ fn transfer( )] /// Scan for existing tape changer devices fn scan(param: Value) -> Result<(), Error> { - let output_format = get_output_format(¶m); let list = linux_tape_changer_list(); @@ -400,14 +377,16 @@ fn scan(param: Value) -> Result<(), Error> { } for item in list.iter() { - println!("{} ({}/{}/{})", item.path, item.vendor, item.model, item.serial); + println!( + "{} ({}/{}/{})", + item.path, item.vendor, item.model, item.serial + ); } Ok(()) } fn main() -> Result<(), Error> { - let uid = nix::unistd::Uid::current(); let username = match nix::unistd::User::from_uid(uid)? { @@ -415,49 +394,47 @@ fn main() -> Result<(), Error> { None => bail!("unable to get user name"), }; - let cmd_def = CliCommandMap::new() .usage_skip_options(&["device", "changer", "output-format"]) .insert( "inquiry", CliCommand::new(&API_METHOD_INQUIRY) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) + .completion_cb("device", complete_changer_path), ) .insert( "inventory", CliCommand::new(&API_METHOD_INVENTORY) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) + .completion_cb("device", complete_changer_path), ) .insert( "load", CliCommand::new(&API_METHOD_LOAD) .arg_param(&["slot"]) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) + .completion_cb("device", complete_changer_path), ) .insert( "unload", CliCommand::new(&API_METHOD_UNLOAD) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) + .completion_cb("device", complete_changer_path), ) .insert("scan", CliCommand::new(&API_METHOD_SCAN)) .insert( "status", CliCommand::new(&API_METHOD_STATUS) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) + .completion_cb("device", complete_changer_path), ) .insert( "transfer", CliCommand::new(&API_METHOD_TRANSFER) .arg_param(&["from", "to"]) .completion_cb("changer", complete_changer_name) - .completion_cb("device", complete_changer_path) - ) - ; + .completion_cb("device", complete_changer_path), + ); let mut rpcenv = CliEnvironment::new(); rpcenv.set_auth_id(Some(format!("{}@pam", username))); diff --git a/pbs-tape/src/blocked_reader.rs b/pbs-tape/src/blocked_reader.rs index b01361f3b..1362eea9e 100644 --- a/pbs-tape/src/blocked_reader.rs +++ b/pbs-tape/src/blocked_reader.rs @@ -1,12 +1,8 @@ use std::io::Read; use crate::{ - TapeRead, - BlockRead, - BlockReadError, + BlockHeader, BlockHeaderFlags, BlockRead, BlockReadError, TapeRead, PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0, - BlockHeader, - BlockHeaderFlags, }; /// Read a block stream generated by 'BlockWriter'. @@ -31,14 +27,12 @@ pub struct BlockedReader { read_pos: usize, } -impl BlockedReader { - +impl BlockedReader { /// Create a new BlockedReader instance. /// /// This tries to read the first block. Please inspect the error /// to detect EOF and EOT. pub fn open(mut reader: R) -> Result { - let mut buffer = BlockHeader::new(); Self::read_block_frame(&mut buffer, &mut reader)?; @@ -67,32 +61,37 @@ impl BlockedReader { } fn check_buffer(buffer: &BlockHeader, seq_nr: u32) -> Result<(usize, bool), std::io::Error> { - if buffer.magic != PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0 { - proxmox_lang::io_bail!("detected tape block with wrong magic number - not written by proxmox tape"); + proxmox_lang::io_bail!( + "detected tape block with wrong magic number - not written by proxmox tape" + ); } if seq_nr != buffer.seq_nr() { proxmox_lang::io_bail!( "detected tape block with wrong sequence number ({} != {})", - seq_nr, buffer.seq_nr()) + seq_nr, + buffer.seq_nr() + ) } let size = buffer.size(); let found_end_marker = buffer.flags.contains(BlockHeaderFlags::END_OF_STREAM); if size > buffer.payload.len() { - proxmox_lang::io_bail!("detected tape block with wrong payload size ({} > {}", size, buffer.payload.len()); + proxmox_lang::io_bail!( + "detected tape block with wrong payload size ({} > {}", + size, + buffer.payload.len() + ); } else if size == 0 && !found_end_marker { proxmox_lang::io_bail!("detected tape block with zero payload size"); } - Ok((size, found_end_marker)) } fn read_block_frame(buffer: &mut BlockHeader, reader: &mut R) -> Result<(), BlockReadError> { - let data = unsafe { std::slice::from_raw_parts_mut( (buffer as *mut BlockHeader) as *mut u8, @@ -115,20 +114,15 @@ impl BlockedReader { Ok(_) => { proxmox_lang::io_bail!("detected tape block after block-stream end marker"); } - Err(BlockReadError::EndOfFile) => { - Ok(()) - } + Err(BlockReadError::EndOfFile) => Ok(()), Err(BlockReadError::EndOfStream) => { proxmox_lang::io_bail!("got unexpected end of tape"); } - Err(BlockReadError::Error(err)) => { - Err(err) - } + Err(BlockReadError::Error(err)) => Err(err), } } fn read_block(&mut self, check_end_marker: bool) -> Result { - match Self::read_block_frame(&mut self.buffer, &mut self.reader) { Ok(()) => { /* ok */ } Err(BlockReadError::EndOfFile) => { @@ -150,7 +144,8 @@ impl BlockedReader { let (size, found_end_marker) = Self::check_buffer(&self.buffer, self.seq_nr)?; self.seq_nr += 1; - if found_end_marker { // consume EOF mark + if found_end_marker { + // consume EOF mark self.found_end_marker = true; self.incomplete = self.buffer.flags.contains(BlockHeaderFlags::INCOMPLETE); Self::consume_eof_marker(&mut self.reader)?; @@ -163,8 +158,7 @@ impl BlockedReader { } } -impl TapeRead for BlockedReader { - +impl TapeRead for BlockedReader { fn is_incomplete(&self) -> Result { if !self.got_eod { proxmox_lang::io_bail!("is_incomplete failed: EOD not reached"); @@ -202,18 +196,17 @@ impl TapeRead for BlockedReader { } } -impl Read for BlockedReader { - +impl Read for BlockedReader { fn read(&mut self, buffer: &mut [u8]) -> Result { - - if self.read_error { + if self.read_error { proxmox_lang::io_bail!("detected read after error - internal error"); } let mut buffer_size = self.buffer.size(); let mut rest = (buffer_size as isize) - (self.read_pos as isize); - if rest <= 0 && !self.got_eod { // try to refill buffer + if rest <= 0 && !self.got_eod { + // try to refill buffer buffer_size = match self.read_block(true) { Ok(len) => len, err => { @@ -232,8 +225,8 @@ impl Read for BlockedReader { } else { rest as usize }; - buffer[..copy_len].copy_from_slice( - &self.buffer.payload[self.read_pos..(self.read_pos + copy_len)]); + buffer[..copy_len] + .copy_from_slice(&self.buffer.payload[self.read_pos..(self.read_pos + copy_len)]); self.read_pos += copy_len; Ok(copy_len) } @@ -242,24 +235,18 @@ impl Read for BlockedReader { #[cfg(test)] mod test { - use std::io::Read; - use anyhow::{bail, Error}; use crate::{ - TapeWrite, - BlockReadError, - EmulateTapeReader, - EmulateTapeWriter, - PROXMOX_TAPE_BLOCK_SIZE, - BlockedReader, - BlockedWriter, + BlockReadError, BlockedReader, BlockedWriter, EmulateTapeReader, EmulateTapeWriter, + TapeWrite, PROXMOX_TAPE_BLOCK_SIZE, }; + use anyhow::{bail, Error}; + use std::io::Read; fn write_and_verify(data: &[u8]) -> Result<(), Error> { - let mut tape_data = Vec::new(); { - let writer = EmulateTapeWriter::new(&mut tape_data, 1024*1024*10); + let writer = EmulateTapeWriter::new(&mut tape_data, 1024 * 1024 * 10); let mut writer = BlockedWriter::new(writer); writer.write_all(data)?; @@ -269,8 +256,8 @@ mod test { assert_eq!( tape_data.len(), - ((data.len() + PROXMOX_TAPE_BLOCK_SIZE)/PROXMOX_TAPE_BLOCK_SIZE) - *PROXMOX_TAPE_BLOCK_SIZE + ((data.len() + PROXMOX_TAPE_BLOCK_SIZE) / PROXMOX_TAPE_BLOCK_SIZE) + * PROXMOX_TAPE_BLOCK_SIZE ); let reader = &mut &tape_data[..]; @@ -299,7 +286,7 @@ mod test { #[test] fn large_data() -> Result<(), Error> { - let data = proxmox_sys::linux::random_data(1024*1024*5)?; + let data = proxmox_sys::linux::random_data(1024 * 1024 * 5)?; write_and_verify(&data) } @@ -309,7 +296,7 @@ mod test { let reader = &mut &tape_data[..]; let reader = EmulateTapeReader::new(reader); match BlockedReader::open(reader) { - Err(BlockReadError::EndOfFile) => { /* OK */ }, + Err(BlockReadError::EndOfFile) => { /* OK */ } _ => bail!("expected EOF"), } @@ -320,7 +307,7 @@ mod test { fn no_end_marker() -> Result<(), Error> { let mut tape_data = Vec::new(); { - let writer = EmulateTapeWriter::new(&mut tape_data, 1024*1024); + let writer = EmulateTapeWriter::new(&mut tape_data, 1024 * 1024); let mut writer = BlockedWriter::new(writer); // write at least one block let data = proxmox_sys::linux::random_data(PROXMOX_TAPE_BLOCK_SIZE)?; @@ -343,7 +330,7 @@ mod test { let mut tape_data = Vec::new(); { - let writer = EmulateTapeWriter::new(&mut tape_data, 1024*1024); + let writer = EmulateTapeWriter::new(&mut tape_data, 1024 * 1024); let mut writer = BlockedWriter::new(writer); writer.write_all(b"ABC")?; diff --git a/pbs-tape/src/blocked_writer.rs b/pbs-tape/src/blocked_writer.rs index 9d5d39e47..7380af243 100644 --- a/pbs-tape/src/blocked_writer.rs +++ b/pbs-tape/src/blocked_writer.rs @@ -1,11 +1,6 @@ use proxmox_io::vec; -use crate::{ - TapeWrite, - BlockWrite, - BlockHeader, - BlockHeaderFlags, -}; +use crate::{BlockHeader, BlockHeaderFlags, BlockWrite, TapeWrite}; /// Assemble and write blocks of data /// @@ -22,8 +17,7 @@ pub struct BlockedWriter { wrote_eof: bool, } -impl Drop for BlockedWriter { - +impl Drop for BlockedWriter { // Try to make sure to end the file with a filemark fn drop(&mut self) { if !self.wrote_eof { @@ -32,8 +26,7 @@ impl Drop for BlockedWriter { } } -impl BlockedWriter { - +impl BlockedWriter { /// Allow access to underlying writer pub fn writer_ref_mut(&mut self) -> &mut W { &mut self.writer @@ -53,7 +46,6 @@ impl BlockedWriter { } fn write_block(buffer: &BlockHeader, writer: &mut W) -> Result { - let data = unsafe { std::slice::from_raw_parts( (buffer as *const BlockHeader) as *const u8, @@ -73,12 +65,13 @@ impl BlockedWriter { } fn write(&mut self, data: &[u8]) -> Result { - - if data.is_empty() { return Ok(0); } + if data.is_empty() { + return Ok(0); + } let rest = self.buffer.payload.len() - self.buffer_pos; let bytes = if data.len() < rest { data.len() } else { rest }; - self.buffer.payload[self.buffer_pos..(self.buffer_pos+bytes)] + self.buffer.payload[self.buffer_pos..(self.buffer_pos + bytes)] .copy_from_slice(&data[..bytes]); let rest = rest - bytes; @@ -89,21 +82,20 @@ impl BlockedWriter { self.buffer.set_seq_nr(self.seq_nr); self.seq_nr += 1; let leom = Self::write_block(&self.buffer, &mut self.writer)?; - if leom { self.logical_end_of_media = true; } + if leom { + self.logical_end_of_media = true; + } self.buffer_pos = 0; self.bytes_written += BlockHeader::SIZE; - } else { self.buffer_pos += bytes; } Ok(bytes) } - } -impl TapeWrite for BlockedWriter { - +impl TapeWrite for BlockedWriter { fn write_all(&mut self, mut data: &[u8]) -> Result { while !data.is_empty() { match self.write(data) { @@ -125,7 +117,9 @@ impl TapeWrite for BlockedWriter { fn finish(&mut self, incomplete: bool) -> Result { vec::clear(&mut self.buffer.payload[self.buffer_pos..]); self.buffer.flags = BlockHeaderFlags::END_OF_STREAM; - if incomplete { self.buffer.flags |= BlockHeaderFlags::INCOMPLETE; } + if incomplete { + self.buffer.flags |= BlockHeaderFlags::INCOMPLETE; + } self.buffer.set_size(self.buffer_pos); self.buffer.set_seq_nr(self.seq_nr); self.seq_nr += 1; @@ -139,5 +133,4 @@ impl TapeWrite for BlockedWriter { fn logical_end_of_media(&self) -> bool { self.logical_end_of_media } - } diff --git a/pbs-tape/src/emulate_tape_reader.rs b/pbs-tape/src/emulate_tape_reader.rs index 6553c3b43..fed10cd85 100644 --- a/pbs-tape/src/emulate_tape_reader.rs +++ b/pbs-tape/src/emulate_tape_reader.rs @@ -12,17 +12,21 @@ pub struct EmulateTapeReader { got_eof: bool, } -impl EmulateTapeReader { - +impl EmulateTapeReader { pub fn new(reader: R) -> Self { - Self { reader, got_eof: false } + Self { + reader, + got_eof: false, + } } } -impl BlockRead for EmulateTapeReader { +impl BlockRead for EmulateTapeReader { fn read_block(&mut self, buffer: &mut [u8]) -> Result { if self.got_eof { - return Err(BlockReadError::Error(proxmox_lang::io_format_err!("detected read after EOF!"))); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "detected read after EOF!" + ))); } match self.reader.read_exact_or_eof(buffer)? { false => { @@ -32,13 +36,11 @@ impl BlockRead for EmulateTapeReader { true => { // test buffer len after EOF test (to allow EOF test with small buffers in BufferedReader) if buffer.len() != PROXMOX_TAPE_BLOCK_SIZE { - return Err(BlockReadError::Error( - proxmox_lang::io_format_err!( - "EmulateTapeReader: read_block with wrong block size ({} != {})", - buffer.len(), - PROXMOX_TAPE_BLOCK_SIZE, - ) - )); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "EmulateTapeReader: read_block with wrong block size ({} != {})", + buffer.len(), + PROXMOX_TAPE_BLOCK_SIZE, + ))); } Ok(buffer.len()) } diff --git a/pbs-tape/src/emulate_tape_writer.rs b/pbs-tape/src/emulate_tape_writer.rs index bae427c7f..7c32c83c4 100644 --- a/pbs-tape/src/emulate_tape_writer.rs +++ b/pbs-tape/src/emulate_tape_writer.rs @@ -14,12 +14,10 @@ pub struct EmulateTapeWriter { wrote_eof: bool, } -impl EmulateTapeWriter { - +impl EmulateTapeWriter { /// Create a new instance allowing to write about max_size bytes pub fn new(writer: W, max_size: usize) -> Self { - - let mut max_blocks = max_size/PROXMOX_TAPE_BLOCK_SIZE; + let mut max_blocks = max_size / PROXMOX_TAPE_BLOCK_SIZE; if max_blocks < 2 { max_blocks = 2; // at least 2 blocks @@ -34,17 +32,20 @@ impl EmulateTapeWriter { } } -impl BlockWrite for EmulateTapeWriter { - +impl BlockWrite for EmulateTapeWriter { fn write_block(&mut self, buffer: &[u8]) -> Result { - if buffer.len() != PROXMOX_TAPE_BLOCK_SIZE { - proxmox_lang::io_bail!("EmulateTapeWriter: got write with wrong block size ({} != {}", - buffer.len(), PROXMOX_TAPE_BLOCK_SIZE); + proxmox_lang::io_bail!( + "EmulateTapeWriter: got write with wrong block size ({} != {}", + buffer.len(), + PROXMOX_TAPE_BLOCK_SIZE + ); } if self.block_nr >= self.max_blocks + 2 { - return Err(io::Error::from_raw_os_error(nix::errno::Errno::ENOSPC as i32)); + return Err(io::Error::from_raw_os_error( + nix::errno::Errno::ENOSPC as i32, + )); } self.writer.write_all(buffer)?; diff --git a/pbs-tape/src/lib.rs b/pbs-tape/src/lib.rs index fadcb530e..eb22569b2 100644 --- a/pbs-tape/src/lib.rs +++ b/pbs-tape/src/lib.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use anyhow::{bail, Error}; use bitflags::bitflags; use endian_trait::Endian; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use serde_json::Value; use proxmox_uuid::Uuid; @@ -30,14 +30,14 @@ mod emulate_tape_reader; pub use emulate_tape_reader::EmulateTapeReader; mod emulate_tape_writer; -pub use emulate_tape_writer::EmulateTapeWriter; +pub use emulate_tape_writer::EmulateTapeWriter; pub mod sg_tape; pub mod sg_pt_changer; /// We use 256KB blocksize (always) -pub const PROXMOX_TAPE_BLOCK_SIZE: usize = 256*1024; +pub const PROXMOX_TAPE_BLOCK_SIZE: usize = 256 * 1024; // openssl::sha::sha256(b"Proxmox Tape Block Header v1.0")[0..8] pub const PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0: [u8; 8] = [220, 189, 175, 202, 235, 160, 165, 40]; @@ -61,7 +61,7 @@ pub const PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0: [u8; 8] = [8, 96, 99, 249, 4 /// header has an additional size field. For streams of blocks, there /// is a sequence number (`seq_nr`) which may be use for additional /// error checking. -#[repr(C,packed)] +#[repr(C, packed)] pub struct BlockHeader { /// fixed value `PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0` pub magic: [u8; 8], @@ -84,7 +84,7 @@ bitflags! { } #[derive(Endian, Copy, Clone, Debug)] -#[repr(C,packed)] +#[repr(C, packed)] /// Media Content Header /// /// All tape files start with this header. The header may contain some @@ -115,11 +115,9 @@ pub struct MediaContentHeader { } impl MediaContentHeader { - /// Create a new instance with autogenerated Uuid pub fn new(content_magic: [u8; 8], size: u32) -> Self { - let uuid = *Uuid::generate() - .into_inner(); + let uuid = *Uuid::generate().into_inner(); Self { magic: PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0, content_magic, @@ -153,9 +151,7 @@ impl MediaContentHeader { } } - impl BlockHeader { - pub const SIZE: usize = PROXMOX_TAPE_BLOCK_SIZE; /// Allocates a new instance on the heap @@ -166,13 +162,9 @@ impl BlockHeader { let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; let mut buffer = unsafe { - let ptr = alloc_zeroed( - Layout::from_size_align(Self::SIZE, page_size) - .unwrap(), - ); + let ptr = alloc_zeroed(Layout::from_size_align(Self::SIZE, page_size).unwrap()); Box::from_raw( - std::slice::from_raw_parts_mut(ptr, Self::SIZE - 16) - as *mut [u8] as *mut Self + std::slice::from_raw_parts_mut(ptr, Self::SIZE - 16) as *mut [u8] as *mut Self, ) }; buffer.magic = PROXMOX_TAPE_BLOCK_HEADER_MAGIC_1_0; @@ -187,7 +179,7 @@ impl BlockHeader { /// Returns the `size` field pub fn size(&self) -> usize { - (self.size[0] as usize) + ((self.size[1] as usize)<<8) + ((self.size[2] as usize)<<16) + (self.size[0] as usize) + ((self.size[1] as usize) << 8) + ((self.size[2] as usize) << 16) } /// Set the `seq_nr` field @@ -263,16 +255,19 @@ pub struct MtxStatus { } impl MtxStatus { - pub fn slot_address(&self, slot: u64) -> Result { if slot == 0 { bail!("invalid slot number '{}' (slots numbers starts at 1)", slot); } if slot > (self.slots.len() as u64) { - bail!("invalid slot number '{}' (max {} slots)", slot, self.slots.len()); + bail!( + "invalid slot number '{}' (max {} slots)", + slot, + self.slots.len() + ); } - Ok(self.slots[(slot -1) as usize].element_address) + Ok(self.slots[(slot - 1) as usize].element_address) } pub fn drive_address(&self, drivenum: u64) -> Result { @@ -287,11 +282,10 @@ impl MtxStatus { // simply use first transport // (are there changers exposing more than one?) // defaults to 0 for changer that do not report transports - self - .transports + self.transports .get(0) .map(|t| t.element_address) - .unwrap_or(0u16) + .unwrap_or(0u16) } pub fn find_free_slot(&self, import_export: bool) -> Option { @@ -301,14 +295,14 @@ impl MtxStatus { continue; // skip slots of wrong type } if let ElementStatus::Empty = slot_info.status { - free_slot = Some((i+1) as u64); + free_slot = Some((i + 1) as u64); break; } } free_slot } - pub fn mark_import_export_slots(&mut self, config: &ScsiTapeChanger) -> Result<(), Error>{ + pub fn mark_import_export_slots(&mut self, config: &ScsiTapeChanger) -> Result<(), Error> { let mut export_slots: HashSet = HashSet::new(); if let Some(slots) = &config.export_slots { diff --git a/pbs-tape/src/linux_list_drives.rs b/pbs-tape/src/linux_list_drives.rs index 60c05218b..cc7765d34 100644 --- a/pbs-tape/src/linux_list_drives.rs +++ b/pbs-tape/src/linux_list_drives.rs @@ -1,8 +1,8 @@ -use std::path::{Path, PathBuf}; use std::collections::HashMap; -use std::fs::{OpenOptions, File}; +use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::AsRawFd; +use std::path::{Path, PathBuf}; use anyhow::{bail, format_err, Error}; use nix::fcntl::{fcntl, FcntlArg, OFlag}; @@ -12,21 +12,20 @@ use proxmox_sys::fs::scan_subdir; use pbs_api_types::{DeviceKind, OptionalDeviceIdentification, TapeDeviceInfo}; -lazy_static::lazy_static!{ +lazy_static::lazy_static! { static ref SCSI_GENERIC_NAME_REGEX: regex::Regex = regex::Regex::new(r"^sg\d+$").unwrap(); } /// List linux tape changer devices pub fn linux_tape_changer_list() -> Vec { - let mut list = Vec::new(); let dir_iter = match scan_subdir( libc::AT_FDCWD, "/sys/class/scsi_generic", - &SCSI_GENERIC_NAME_REGEX) - { + &SCSI_GENERIC_NAME_REGEX, + ) { Err(_) => return list, Ok(iter) => iter, }; @@ -63,7 +62,9 @@ pub fn linux_tape_changer_list() -> Vec { continue; } } - _ => { continue; } + _ => { + continue; + } } // let mut test_path = sys_path.clone(); @@ -75,22 +76,42 @@ pub fn linux_tape_changer_list() -> Vec { Some(dev_path) => dev_path, }; - let serial = match device.property_value("ID_SCSI_SERIAL") + let serial = match device + .property_value("ID_SCSI_SERIAL") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) - { + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) { None => continue, Some(serial) => serial, }; - let vendor = device.property_value("ID_VENDOR") + let vendor = device + .property_value("ID_VENDOR") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) .unwrap_or_else(|| String::from("unknown")); - let model = device.property_value("ID_MODEL") + let model = device + .property_value("ID_MODEL") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) .unwrap_or_else(|| String::from("unknown")); let dev_path = format!("/dev/tape/by-id/scsi-{}", serial); @@ -113,14 +134,13 @@ pub fn linux_tape_changer_list() -> Vec { /// List LTO drives pub fn lto_tape_device_list() -> Vec { - let mut list = Vec::new(); let dir_iter = match scan_subdir( libc::AT_FDCWD, "/sys/class/scsi_generic", - &SCSI_GENERIC_NAME_REGEX) - { + &SCSI_GENERIC_NAME_REGEX, + ) { Err(_) => return list, Ok(iter) => iter, }; @@ -157,7 +177,9 @@ pub fn lto_tape_device_list() -> Vec { continue; } } - _ => { continue; } + _ => { + continue; + } } // let mut test_path = sys_path.clone(); @@ -169,22 +191,42 @@ pub fn lto_tape_device_list() -> Vec { Some(dev_path) => dev_path, }; - let serial = match device.property_value("ID_SCSI_SERIAL") + let serial = match device + .property_value("ID_SCSI_SERIAL") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) - { + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) { None => continue, Some(serial) => serial, }; - let vendor = device.property_value("ID_VENDOR") + let vendor = device + .property_value("ID_VENDOR") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) .unwrap_or_else(|| String::from("unknown")); - let model = device.property_value("ID_MODEL") + let model = device + .property_value("ID_MODEL") .map(std::ffi::OsString::from) - .and_then(|s| if let Ok(s) = s.into_string() { Some(s) } else { None }) + .and_then(|s| { + if let Ok(s) = s.into_string() { + Some(s) + } else { + None + } + }) .unwrap_or_else(|| String::from("unknown")); let dev_path = format!("/dev/tape/by-id/scsi-{}-sg", serial); @@ -206,17 +248,14 @@ pub fn lto_tape_device_list() -> Vec { } /// Test if a device exists, and returns associated `TapeDeviceInfo` -pub fn lookup_device<'a>( - devices: &'a[TapeDeviceInfo], - path: &str, -) -> Option<&'a TapeDeviceInfo> { - +pub fn lookup_device<'a>(devices: &'a [TapeDeviceInfo], path: &str) -> Option<&'a TapeDeviceInfo> { if let Ok(stat) = nix::sys::stat::stat(path) { - let major = unsafe { libc::major(stat.st_rdev) }; let minor = unsafe { libc::minor(stat.st_rdev) }; - devices.iter().find(|d| d.major == major && d.minor == minor) + devices + .iter() + .find(|d| d.major == major && d.minor == minor) } else { None } @@ -224,10 +263,9 @@ pub fn lookup_device<'a>( /// Lookup optional drive identification attributes pub fn lookup_device_identification<'a>( - devices: &'a[TapeDeviceInfo], + devices: &'a [TapeDeviceInfo], path: &str, ) -> OptionalDeviceIdentification { - if let Some(info) = lookup_device(devices, path) { OptionalDeviceIdentification { vendor: Some(info.vendor.clone()), @@ -244,20 +282,15 @@ pub fn lookup_device_identification<'a>( } /// Make sure path is a lto tape device -pub fn check_drive_path( - drives: &[TapeDeviceInfo], - path: &str, -) -> Result<(), Error> { +pub fn check_drive_path(drives: &[TapeDeviceInfo], path: &str) -> Result<(), Error> { if lookup_device(drives, path).is_none() { bail!("path '{}' is not a lto SCSI-generic tape device", path); } Ok(()) } - /// Check for correct Major/Minor numbers pub fn check_tape_is_lto_tape_device(file: &File) -> Result<(), Error> { - let stat = nix::sys::stat::fstat(file.as_raw_fd())?; let devnum = stat.st_rdev; @@ -281,10 +314,7 @@ pub fn check_tape_is_lto_tape_device(file: &File) -> Result<(), Error> { /// The open call use O_NONBLOCK, but that flag is cleard after open /// succeeded. This also checks if the device is a non-rewinding tape /// device. -pub fn open_lto_tape_device( - path: &str, -) -> Result { - +pub fn open_lto_tape_device(path: &str) -> Result { let file = OpenOptions::new() .read(true) .write(true) @@ -293,14 +323,12 @@ pub fn open_lto_tape_device( // clear O_NONBLOCK from now on. - let flags = fcntl(file.as_raw_fd(), FcntlArg::F_GETFL) - .into_io_result()?; + let flags = fcntl(file.as_raw_fd(), FcntlArg::F_GETFL).into_io_result()?; let mut flags = OFlag::from_bits_truncate(flags); flags.remove(OFlag::O_NONBLOCK); - fcntl(file.as_raw_fd(), FcntlArg::F_SETFL(flags)) - .into_io_result()?; + fcntl(file.as_raw_fd(), FcntlArg::F_SETFL(flags)).into_io_result()?; check_tape_is_lto_tape_device(&file) .map_err(|err| format_err!("device type check {:?} failed - {}", path, err))?; @@ -308,15 +336,20 @@ pub fn open_lto_tape_device( Ok(file) } - // shell completion helper /// List changer device paths pub fn complete_changer_path(_arg: &str, _param: &HashMap) -> Vec { - linux_tape_changer_list().iter().map(|v| v.path.clone()).collect() + linux_tape_changer_list() + .iter() + .map(|v| v.path.clone()) + .collect() } /// List tape device paths pub fn complete_drive_path(_arg: &str, _param: &HashMap) -> Vec { - lto_tape_device_list().iter().map(|v| v.path.clone()).collect() + lto_tape_device_list() + .iter() + .map(|v| v.path.clone()) + .collect() } diff --git a/pbs-tape/src/sg_pt_changer.rs b/pbs-tape/src/sg_pt_changer.rs index d0f9c3677..eb581d6c2 100644 --- a/pbs-tape/src/sg_pt_changer.rs +++ b/pbs-tape/src/sg_pt_changer.rs @@ -1,9 +1,9 @@ //! SCSI changer implementation using libsgutil2 -use std::os::unix::prelude::AsRawFd; -use std::io::Read; use std::collections::HashMap; +use std::fs::{File, OpenOptions}; +use std::io::Read; +use std::os::unix::prelude::AsRawFd; use std::path::Path; -use std::fs::{OpenOptions, File}; use anyhow::{bail, format_err, Error}; use endian_trait::Endian; @@ -13,31 +13,25 @@ use proxmox_io::ReadExt; use pbs_api_types::ScsiTapeChanger; use crate::{ - ElementStatus,MtxStatus,TransportElementStatus,DriveStatus,StorageElementStatus, - sgutils2::{ - SgRaw, - SENSE_KEY_NOT_READY, - ScsiError, - scsi_ascii_to_string, - scsi_inquiry, - }, + sgutils2::{scsi_ascii_to_string, scsi_inquiry, ScsiError, SgRaw, SENSE_KEY_NOT_READY}, + DriveStatus, ElementStatus, MtxStatus, StorageElementStatus, TransportElementStatus, }; -const SCSI_CHANGER_DEFAULT_TIMEOUT: usize = 60*5; // 5 minutes +const SCSI_CHANGER_DEFAULT_TIMEOUT: usize = 60 * 5; // 5 minutes const SCSI_VOLUME_TAG_LEN: usize = 36; /// Initialize element status (Inventory) pub fn initialize_element_status(file: &mut F) -> Result<(), Error> { - let mut sg_raw = SgRaw::new(file, 64)?; // like mtx(1), set a very long timeout (30 minutes) - sg_raw.set_timeout(30*60); + sg_raw.set_timeout(30 * 60); let mut cmd = Vec::new(); cmd.extend(&[0x07, 0, 0, 0, 0, 0]); // INITIALIZE ELEMENT STATUS (07h) - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("initializte element status (07h) failed - {}", err))?; Ok(()) @@ -78,7 +72,6 @@ fn execute_scsi_command( error_prefix: &str, retry: bool, ) -> Result, Error> { - let start = std::time::SystemTime::now(); let mut last_msg: Option = None; @@ -103,9 +96,12 @@ fn execute_scsi_command( if let ScsiError::Sense(ref sense) = err { // Not Ready - becoming ready - if sense.sense_key == SENSE_KEY_NOT_READY && sense.asc == 0x04 && sense.ascq == 1 { + if sense.sense_key == SENSE_KEY_NOT_READY + && sense.asc == 0x04 + && sense.ascq == 1 + { // wait up to 5 minutes, long enough to finish inventorize - timeout = std::time::Duration::new(5*60, 0); + timeout = std::time::Duration::new(5 * 60, 0); } } @@ -117,14 +113,12 @@ fn execute_scsi_command( continue; // try again } } - } + } } - fn read_element_address_assignment( file: &mut F, ) -> Result { - let allocation_len: u8 = u8::MAX; let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT); @@ -148,7 +142,8 @@ fn read_element_address_assignment( } Ok(page) - }).map_err(|err: Error| format_err!("decode element address assignment page failed - {}", err)) + }) + .map_err(|err: Error| format_err!("decode element address assignment page failed - {}", err)) } fn scsi_move_medium_cdb( @@ -156,7 +151,6 @@ fn scsi_move_medium_cdb( source_element_address: u16, destination_element_address: u16, ) -> Vec { - let mut cmd = Vec::new(); cmd.push(0xA5); // MOVE MEDIUM (A5h) cmd.push(0); // reserved @@ -172,11 +166,7 @@ fn scsi_move_medium_cdb( } /// Load media from storage slot into drive -pub fn load_slot( - file: &mut File, - from_slot: u64, - drivenum: u64, -) -> Result<(), Error> { +pub fn load_slot(file: &mut File, from_slot: u64, drivenum: u64) -> Result<(), Error> { let status = read_element_status(file)?; let transport_address = status.transport_address(); @@ -192,19 +182,15 @@ pub fn load_slot( let mut sg_raw = SgRaw::new(file, 64)?; sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT); - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("load drive failed - {}", err))?; Ok(()) } /// Unload media from drive into a storage slot -pub fn unload( - file: &mut File, - to_slot: u64, - drivenum: u64, -) -> Result<(), Error> { - +pub fn unload(file: &mut File, to_slot: u64, drivenum: u64) -> Result<(), Error> { let status = read_element_status(file)?; let transport_address = status.transport_address(); @@ -220,7 +206,8 @@ pub fn unload( let mut sg_raw = SgRaw::new(file, 64)?; sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT); - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("unload drive failed - {}", err))?; Ok(()) @@ -232,7 +219,6 @@ pub fn transfer_medium( from_slot: u64, to_slot: u64, ) -> Result<(), Error> { - let status = read_element_status(file)?; let transport_address = status.transport_address(); @@ -248,11 +234,14 @@ pub fn transfer_medium( let mut sg_raw = SgRaw::new(file, 64)?; sg_raw.set_timeout(SCSI_CHANGER_DEFAULT_TIMEOUT); - sg_raw.do_command(&cmd) - .map_err(|err| { - format_err!("transfer medium from slot {} to slot {} failed - {}", - from_slot, to_slot, err) - })?; + sg_raw.do_command(&cmd).map_err(|err| { + format_err!( + "transfer medium from slot {} to slot {} failed - {}", + from_slot, + to_slot, + err + ) + })?; Ok(()) } @@ -282,7 +271,7 @@ impl ElementType { fn byte6(&self) -> u8 { match *self { ElementType::DataTransferWithDVCID => 0b001, // Mixed=0,CurData=0,DVCID=1 - _ => 0b000, // Mixed=0,CurData=0,DVCID=0 + _ => 0b000, // Mixed=0,CurData=0,DVCID=0 } } } @@ -293,7 +282,6 @@ fn scsi_read_element_status_cdb( element_type: ElementType, allocation_len: u32, ) -> Vec { - let mut cmd = Vec::new(); cmd.push(0xB8); // READ ELEMENT STATUS (B8h) cmd.push(element_type.byte1()); @@ -315,7 +303,6 @@ fn get_element( allocation_len: u32, mut retry: bool, ) -> Result { - let mut start_element_address = 0; let number_of_elements: u16 = 1000; // some changers limit the query @@ -328,7 +315,12 @@ fn get_element( }; loop { - let cmd = scsi_read_element_status_cdb(start_element_address, number_of_elements, element_type, allocation_len); + let cmd = scsi_read_element_status_cdb( + start_element_address, + number_of_elements, + element_type, + allocation_len, + ); let data = execute_scsi_command(sg_raw, &cmd, "read element status (B8h)", retry)?; @@ -364,7 +356,6 @@ fn get_element( /// Read element status. pub fn read_element_status(file: &mut F) -> Result { - let inquiry = scsi_inquiry(file)?; if inquiry.peripheral_type != 8 { @@ -387,15 +378,30 @@ pub fn read_element_status(file: &mut F) -> Result let page = get_element(&mut sg_raw, ElementType::Storage, allocation_len, true)?; storage_slots.extend(page.storage_slots); - let page = get_element(&mut sg_raw, ElementType::ImportExport, allocation_len, false)?; + let page = get_element( + &mut sg_raw, + ElementType::ImportExport, + allocation_len, + false, + )?; import_export_slots.extend(page.import_export_slots); - let page = get_element(&mut sg_raw, ElementType::DataTransfer, allocation_len, false)?; + let page = get_element( + &mut sg_raw, + ElementType::DataTransfer, + allocation_len, + false, + )?; drives.extend(page.drives); // get the serial + vendor + model, // some changer require this to be an extra scsi command - let page = get_element(&mut sg_raw, ElementType::DataTransferWithDVCID, allocation_len, false)?; + let page = get_element( + &mut sg_raw, + ElementType::DataTransferWithDVCID, + allocation_len, + false, + )?; // should be in same order and same count, but be on the safe side. // there should not be too many drives normally for drive in drives.iter_mut() { @@ -408,7 +414,12 @@ pub fn read_element_status(file: &mut F) -> Result } } - let page = get_element(&mut sg_raw, ElementType::MediumTransport, allocation_len, false)?; + let page = get_element( + &mut sg_raw, + ElementType::MediumTransport, + allocation_len, + false, + )?; transports.extend(page.transports); let transport_count = setup.transport_element_count as usize; @@ -451,14 +462,18 @@ pub fn read_element_status(file: &mut F) -> Result let mut slots = storage_slots; slots.extend(import_export_slots); - let mut status = MtxStatus { transports, drives, slots }; + let mut status = MtxStatus { + transports, + drives, + slots, + }; // sanity checks if status.drives.is_empty() { bail!("no data transfer elements reported"); } if status.slots.is_empty() { - bail!("no storage elements reported"); + bail!("no storage elements reported"); } // compute virtual storage slot to element_address map @@ -482,8 +497,7 @@ pub fn read_element_status(file: &mut F) -> Result pub fn status(config: &ScsiTapeChanger) -> Result { let path = &config.path; - let mut file = open(path) - .map_err(|err| format_err!("error opening '{}': {}", path, err))?; + let mut file = open(path).map_err(|err| format_err!("error opening '{}': {}", path, err))?; let mut status = read_element_status(&mut file) .map_err(|err| format_err!("error reading element status: {}", err))?; @@ -492,14 +506,13 @@ pub fn status(config: &ScsiTapeChanger) -> Result { Ok(status) } - #[repr(C, packed)] #[derive(Endian)] struct ElementStatusHeader { first_element_address_reported: u16, number_of_elements_available: u16, reserved: u8, - byte_count_of_report_available: [u8;3], + byte_count_of_report_available: [u8; 3], } #[repr(C, packed)] @@ -509,18 +522,17 @@ struct SubHeader { flags: u8, descriptor_length: u16, reserved: u8, - byte_count_of_descriptor_data_available: [u8;3], + byte_count_of_descriptor_data_available: [u8; 3], } impl SubHeader { - fn parse_optional_volume_tag( &self, reader: &mut R, full: bool, ) -> Result, Error> { - - if (self.flags & 128) != 0 { // has PVolTag + if (self.flags & 128) != 0 { + // has PVolTag let tmp = reader.read_exact_allocated(SCSI_VOLUME_TAG_LEN)?; if full { let volume_tag = scsi_ascii_to_string(&tmp); @@ -532,12 +544,9 @@ impl SubHeader { // AFAIK, tape changer do not use AlternateVolumeTag // but parse anyways, just to be sure - fn skip_alternate_volume_tag( - &self, - reader: &mut R, - ) -> Result, Error> { - - if (self.flags & 64) != 0 { // has AVolTag + fn skip_alternate_volume_tag(&self, reader: &mut R) -> Result, Error> { + if (self.flags & 64) != 0 { + // has AVolTag let _tmp = reader.read_exact_allocated(SCSI_VOLUME_TAG_LEN)?; } @@ -547,13 +556,14 @@ impl SubHeader { #[repr(C, packed)] #[derive(Endian)] -struct TransportDescriptor { // Robot/Griper +struct TransportDescriptor { + // Robot/Griper element_address: u16, flags1: u8, reserved_3: u8, additional_sense_code: u8, additional_sense_code_qualifier: u8, - reserved_6: [u8;3], + reserved_6: [u8; 3], flags2: u8, source_storage_element_address: u16, // volume tag and Mixed media descriptor follows (depends on flags) @@ -561,7 +571,8 @@ struct TransportDescriptor { // Robot/Griper #[repr(C, packed)] #[derive(Endian)] -struct TransferDescriptor { // Tape drive +struct TransferDescriptor { + // Tape drive element_address: u16, flags1: u8, reserved_3: u8, @@ -578,7 +589,8 @@ struct TransferDescriptor { // Tape drive #[repr(C, packed)] #[derive(Endian)] -struct DvcidHead { // Drive Identifier Header +struct DvcidHead { + // Drive Identifier Header code_set: u8, identifier_type: u8, reserved: u8, @@ -588,13 +600,14 @@ struct DvcidHead { // Drive Identifier Header #[repr(C, packed)] #[derive(Endian)] -struct StorageDescriptor { // Mail Slot +struct StorageDescriptor { + // Mail Slot element_address: u16, flags1: u8, reserved_3: u8, additional_sense_code: u8, additional_sense_code_qualifier: u8, - reserved_6: [u8;3], + reserved_6: [u8; 3], flags2: u8, source_storage_element_address: u16, // volume tag and Mixed media descriptor follows (depends on flags) @@ -630,7 +643,8 @@ fn decode_dvcid_info(reader: &mut R) -> Result { let dvcid: DvcidHead = unsafe { reader.read_be_value()? }; let (serial, vendor, model) = match (dvcid.code_set, dvcid.identifier_type) { - (2, 0) => { // Serial number only (Quantum Superloader3 uses this) + (2, 0) => { + // Serial number only (Quantum Superloader3 uses this) let serial = reader.read_exact_allocated(dvcid.identifier_len as usize)?; let serial = scsi_ascii_to_string(&serial); (Some(serial), None, None) @@ -661,9 +675,7 @@ fn decode_element_status_page( data: &[u8], start_element_address: u16, ) -> Result { - proxmox_lang::try_block!({ - let mut result = DecodedStatusPage { last_element_address: None, transports: Vec::new(), @@ -690,7 +702,11 @@ fn decode_element_status_page( if len < reader.len() { reader = &reader[..len]; } else if len > reader.len() { - bail!("wrong amount of data: expected {}, got {}", len, reader.len()); + bail!( + "wrong amount of data: expected {}, got {}", + len, + reader.len() + ); } loop { @@ -763,7 +779,8 @@ fn decode_element_status_page( 4 => { let desc: TransferDescriptor = unsafe { reader.read_be_value()? }; - let loaded_slot = if (desc.flags2 & 128) != 0 { // SValid + let loaded_slot = if (desc.flags2 & 128) != 0 { + // SValid Some(desc.source_storage_element_address as u64) } else { None @@ -798,23 +815,21 @@ fn decode_element_status_page( } Ok(result) - }).map_err(|err: Error| format_err!("decode element status failed - {}", err)) + }) + .map_err(|err: Error| format_err!("decode element status failed - {}", err)) } /// Open the device for read/write, returns the file handle pub fn open>(path: P) -> Result { - let file = OpenOptions::new() - .read(true) - .write(true) - .open(path)?; + let file = OpenOptions::new().read(true).write(true).open(path)?; Ok(file) } #[cfg(test)] mod test { - use anyhow::Error; use super::*; + use anyhow::Error; struct StorageDesc { address: u16, @@ -826,9 +841,10 @@ mod test { trailing: &[u8], element_type: u8, ) -> Vec { - let descs: Vec> = descriptors.iter().map(|desc| { - build_storage_descriptor(desc, trailing) - }).collect(); + let descs: Vec> = descriptors + .iter() + .map(|desc| build_storage_descriptor(desc, trailing)) + .collect(); let (desc_len, address) = if let Some(el) = descs.get(0) { (el.len() as u16, descriptors[0].address) @@ -863,10 +879,7 @@ mod test { res } - fn build_storage_descriptor( - desc: &StorageDesc, - trailing: &[u8], - ) -> Vec { + fn build_storage_descriptor(desc: &StorageDesc, trailing: &[u8]) -> Vec { let mut res = Vec::new(); res.push(((desc.address >> 8) & 0xFF) as u8); res.push((desc.address & 0xFF) as u8); @@ -876,7 +889,7 @@ mod test { res.push(0x00); // full } - res.extend_from_slice(&[0,0,0,0,0,0,0x80]); + res.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0x80]); res.push(((desc.address >> 8) & 0xFF) as u8); res.push((desc.address & 0xFF) as u8); @@ -942,7 +955,7 @@ mod test { pvoltag: Some("1234567890".to_string()), }, ]; - let test_data = build_element_status_page(descs, &[0,0,0,0,0], 0x2); + let test_data = build_element_status_page(descs, &[0, 0, 0, 0, 0], 0x2); let page = decode_element_status_page(&test_data, 0)?; assert_eq!(page.storage_slots.len(), 2); Ok(()) diff --git a/pbs-tape/src/sg_tape.rs b/pbs-tape/src/sg_tape.rs index e05906716..776ee06fa 100644 --- a/pbs-tape/src/sg_tape.rs +++ b/pbs-tape/src/sg_tape.rs @@ -1,10 +1,10 @@ -use std::time::SystemTime; +use std::convert::TryFrom; +use std::convert::TryInto; use std::fs::{File, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::AsRawFd; use std::path::Path; -use std::convert::TryFrom; -use std::convert::TryInto; +use std::time::SystemTime; use anyhow::{bail, format_err, Error}; use endian_trait::Endian; @@ -25,56 +25,43 @@ pub use mam::*; mod report_density; pub use report_density::*; -use proxmox_sys::error::SysResult; use proxmox_io::{ReadExt, WriteExt}; +use proxmox_sys::error::SysResult; -use pbs_api_types::{MamAttribute, Lp17VolumeStatistics, LtoDriveAndMediaStatus}; +use pbs_api_types::{Lp17VolumeStatistics, LtoDriveAndMediaStatus, MamAttribute}; use crate::{ - BlockRead, - BlockReadError, - BlockWrite, - BlockedWriter, - BlockedReader, sgutils2::{ - SgRaw, - SenseInfo, - ScsiError, - InquiryInfo, - ModeParameterHeader, - ModeBlockDescriptor, - alloc_page_aligned_buffer, - scsi_inquiry, - scsi_mode_sense, - scsi_request_sense, + alloc_page_aligned_buffer, scsi_inquiry, scsi_mode_sense, scsi_request_sense, InquiryInfo, + ModeBlockDescriptor, ModeParameterHeader, ScsiError, SenseInfo, SgRaw, }, + BlockRead, BlockReadError, BlockWrite, BlockedReader, BlockedWriter, }; #[repr(C, packed)] #[derive(Endian, Debug, Copy, Clone)] pub struct ReadPositionLongPage { flags: u8, - reserved: [u8;3], + reserved: [u8; 3], partition_number: u32, pub logical_object_number: u64, pub logical_file_id: u64, - obsolete: [u8;8], + obsolete: [u8; 8], } #[repr(C, packed)] #[derive(Endian, Debug, Copy, Clone)] struct DataCompressionModePage { page_code: u8, // 0x0f - page_length: u8, // 0x0e + page_length: u8, // 0x0e flags2: u8, flags3: u8, compression_algorithm: u32, decompression_algorithm: u32, - reserved: [u8;4], + reserved: [u8; 4], } impl DataCompressionModePage { - pub fn set_compression(&mut self, enable: bool) { if enable { self.flags2 |= 128; @@ -92,17 +79,15 @@ impl DataCompressionModePage { #[derive(Endian)] struct MediumConfigurationModePage { page_code: u8, // 0x1d - page_length: u8, // 0x1e + page_length: u8, // 0x1e flags2: u8, - reserved: [u8;29], + reserved: [u8; 29], } impl MediumConfigurationModePage { - pub fn is_worm(&self) -> bool { (self.flags2 & 1) == 1 } - } #[derive(Debug)] @@ -122,18 +107,19 @@ pub struct SgTape { } impl SgTape { - - const SCSI_TAPE_DEFAULT_TIMEOUT: usize = 60*10; // 10 minutes + const SCSI_TAPE_DEFAULT_TIMEOUT: usize = 60 * 10; // 10 minutes /// Create a new instance /// /// Uses scsi_inquiry to check the device type. pub fn new(mut file: File) -> Result { - let info = scsi_inquiry(&mut file)?; if info.peripheral_type != 1 { - bail!("not a tape device (peripheral_type = {})", info.peripheral_type); + bail!( + "not a tape device (peripheral_type = {})", + info.peripheral_type + ); } Ok(Self { @@ -169,14 +155,12 @@ impl SgTape { .open(path)?; // then clear O_NONBLOCK - let flags = fcntl(file.as_raw_fd(), FcntlArg::F_GETFL) - .into_io_result()?; + let flags = fcntl(file.as_raw_fd(), FcntlArg::F_GETFL).into_io_result()?; let mut flags = OFlag::from_bits_truncate(flags); flags.remove(OFlag::O_NONBLOCK); - fcntl(file.as_raw_fd(), FcntlArg::F_SETFL(flags)) - .into_io_result()?; + fcntl(file.as_raw_fd(), FcntlArg::F_SETFL(flags)).into_io_result()?; Self::new(file) } @@ -203,7 +187,8 @@ impl SgTape { } cmd.extend(&[0, 0, 0, 0]); - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("erase failed - {}", err))?; Ok(()) @@ -211,7 +196,6 @@ impl SgTape { /// Format media, single partition pub fn format_media(&mut self, fast: bool) -> Result<(), Error> { - // try to get info about loaded media first let (has_format, is_worm) = match self.read_medium_configuration_page() { Ok((_head, block_descriptor, page)) => { @@ -236,7 +220,6 @@ impl SgTape { } Ok(()) - } else { self.rewind()?; @@ -261,7 +244,6 @@ impl SgTape { /// Lock/Unlock drive door pub fn set_medium_removal(&mut self, allow: bool) -> Result<(), ScsiError> { - let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); @@ -279,19 +261,19 @@ impl SgTape { } pub fn rewind(&mut self) -> Result<(), Error> { - let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); cmd.extend(&[0x01, 0, 0, 0, 0, 0]); // REWIND - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("rewind failed - {}", err))?; Ok(()) } - pub fn locate_file(&mut self, position: u64) -> Result<(), Error> { + pub fn locate_file(&mut self, position: u64) -> Result<(), Error> { if position == 0 { return self.rewind(); } @@ -303,7 +285,8 @@ impl SgTape { self.rewind()?; let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); - sg_raw.do_command(SPACE_ONE_FILEMARK) + sg_raw + .do_command(SPACE_ONE_FILEMARK) .map_err(|err| format_err!("locate file {} (space) failed - {}", position, err))?; return Ok(()); } @@ -334,20 +317,22 @@ impl SgTape { cmd.extend(&fixed_position.to_be_bytes()); cmd.extend(&[0, 0, 0, 0]); - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("locate file {} failed - {}", position, err))?; // LOCATE always position at the BOT side of the filemark, so // we need to move to other side of filemark - sg_raw.do_command(SPACE_ONE_FILEMARK) + sg_raw + .do_command(SPACE_ONE_FILEMARK) .map_err(|err| format_err!("locate file {} (space) failed - {}", position, err))?; if self.locate_offset.is_none() { // check if we landed at correct position let current_file = self.current_file_number()?; if current_file != position { - let offset: i64 = - i64::try_from((position as i128) - (current_file as i128)).map_err(|err| { + let offset: i64 = i64::try_from((position as i128) - (current_file as i128)) + .map_err(|err| { format_err!( "locate_file: offset between {} and {} invalid: {}", position, @@ -370,7 +355,6 @@ impl SgTape { } pub fn position(&mut self) -> Result { - let expected_size = std::mem::size_of::(); let mut sg_raw = SgRaw::new(&mut self.file, 32)?; @@ -381,12 +365,17 @@ impl SgTape { // reference manual. cmd.extend(&[0x34, 0x06, 0, 0, 0, 0, 0, 0, 0, 0]); // READ POSITION LONG FORM - let data = sg_raw.do_command(&cmd) + let data = sg_raw + .do_command(&cmd) .map_err(|err| format_err!("read position failed - {}", err))?; let page = proxmox_lang::try_block!({ if data.len() != expected_size { - bail!("got unexpected data len ({} != {}", data.len(), expected_size); + bail!( + "got unexpected data len ({} != {}", + data.len(), + expected_size + ); } let mut reader = data; @@ -394,7 +383,8 @@ impl SgTape { let page: ReadPositionLongPage = unsafe { reader.read_be_value()? }; Ok(page) - }).map_err(|err: Error| format_err!("decode position page failed - {}", err))?; + }) + .map_err(|err: Error| format_err!("decode position page failed - {}", err))?; if page.partition_number != 0 { bail!("detecthed partitioned tape - not supported"); @@ -410,7 +400,6 @@ impl SgTape { /// Check if we are positioned after a filemark (or BOT) pub fn check_filemark(&mut self) -> Result { - let pos = self.position()?; if pos.logical_object_number == 0 { // at BOT, Ok (no filemark required) @@ -421,13 +410,24 @@ impl SgTape { match self.space(-1, true) { Ok(_) => { self.space(1, true) // move back to end - .map_err(|err| format_err!("check_filemark failed (space forward) - {}", err))?; + .map_err(|err| { + format_err!("check_filemark failed (space forward) - {}", err) + })?; Ok(false) } - Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 1 })) => { + Err(ScsiError::Sense(SenseInfo { + sense_key: 0, + asc: 0, + ascq: 1, + })) => { // Filemark detected - good self.space(1, false) // move to EOT side of filemark - .map_err(|err| format_err!("check_filemark failed (move to EOT side of filemark) - {}", err))?; + .map_err(|err| { + format_err!( + "check_filemark failed (move to EOT side of filemark) - {}", + err + ) + })?; Ok(true) } Err(err) => { @@ -436,13 +436,14 @@ impl SgTape { } } - pub fn move_to_eom(&mut self, write_missing_eof: bool) -> Result<(), Error> { + pub fn move_to_eom(&mut self, write_missing_eof: bool) -> Result<(), Error> { let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); cmd.extend(&[0x11, 0x03, 0, 0, 0, 0]); // SPACE(6) move to EOD - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("move to EOD failed - {}", err))?; if write_missing_eof && !self.check_filemark()? { @@ -452,7 +453,7 @@ impl SgTape { Ok(()) } - fn space(&mut self, count: isize, blocks: bool) -> Result<(), ScsiError> { + fn space(&mut self, count: isize, blocks: bool) -> Result<(), ScsiError> { let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); @@ -487,52 +488,50 @@ impl SgTape { Ok(()) } - pub fn space_filemarks(&mut self, count: isize) -> Result<(), Error> { + pub fn space_filemarks(&mut self, count: isize) -> Result<(), Error> { self.space(count, false) .map_err(|err| format_err!("space filemarks failed - {}", err)) } - pub fn space_blocks(&mut self, count: isize) -> Result<(), Error> { + pub fn space_blocks(&mut self, count: isize) -> Result<(), Error> { self.space(count, true) .map_err(|err| format_err!("space blocks failed - {}", err)) } - pub fn eject(&mut self) -> Result<(), Error> { + pub fn eject(&mut self) -> Result<(), Error> { let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); cmd.extend(&[0x1B, 0, 0, 0, 0, 0]); // LODA/UNLOAD HOLD=0, LOAD=0 - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("eject failed - {}", err))?; Ok(()) } - pub fn load(&mut self) -> Result<(), Error> { + pub fn load(&mut self) -> Result<(), Error> { let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); cmd.extend(&[0x1B, 0, 0, 0, 0b0000_0001, 0]); // LODA/UNLOAD HOLD=0, LOAD=1 - sg_raw.do_command(&cmd) + sg_raw + .do_command(&cmd) .map_err(|err| format_err!("load media failed - {}", err))?; Ok(()) } - pub fn write_filemarks( - &mut self, - count: usize, - immediate: bool, - ) -> Result<(), std::io::Error> { - + pub fn write_filemarks(&mut self, count: usize, immediate: bool) -> Result<(), std::io::Error> { if count > 255 { proxmox_lang::io_bail!("write_filemarks failed: got strange count '{}'", count); } - let mut sg_raw = SgRaw::new(&mut self.file, 16) - .map_err(|err| proxmox_lang::io_format_err!("write_filemarks failed (alloc) - {}", err))?; + let mut sg_raw = SgRaw::new(&mut self.file, 16).map_err(|err| { + proxmox_lang::io_format_err!("write_filemarks failed (alloc) - {}", err) + })?; sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); @@ -547,9 +546,11 @@ impl SgTape { match sg_raw.do_command(&cmd) { Ok(_) => { /* OK */ } - Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 2 })) => { - /* LEOM - ignore */ - } + Err(ScsiError::Sense(SenseInfo { + sense_key: 0, + asc: 0, + ascq: 2, + })) => { /* LEOM - ignore */ } Err(err) => { proxmox_lang::io_bail!("write filemark failed - {}", err); } @@ -565,7 +566,6 @@ impl SgTape { } pub fn test_unit_ready(&mut self) -> Result<(), Error> { - let mut sg_raw = SgRaw::new(&mut self.file, 16)?; sg_raw.set_timeout(30); // use short timeout let mut cmd = Vec::new(); @@ -580,7 +580,6 @@ impl SgTape { } pub fn wait_until_ready(&mut self) -> Result<(), Error> { - let start = SystemTime::now(); let max_wait = std::time::Duration::new(Self::SCSI_TAPE_DEFAULT_TIMEOUT as u64, 0); @@ -612,11 +611,7 @@ impl SgTape { read_volume_statistics(&mut self.file) } - pub fn set_encryption( - &mut self, - key: Option<[u8; 32]>, - ) -> Result<(), Error> { - + pub fn set_encryption(&mut self, key: Option<[u8; 32]>) -> Result<(), Error> { self.encryption_key_loaded = key.is_some(); set_encryption(&mut self.file, key) @@ -626,19 +621,17 @@ impl SgTape { // // Returns true if the drive reached the Logical End Of Media (early warning) fn write_block(&mut self, data: &[u8]) -> Result { - let transfer_len = data.len(); if transfer_len > 0x800000 { - proxmox_lang::io_bail!("write failed - data too large"); + proxmox_lang::io_bail!("write failed - data too large"); } - let mut sg_raw = SgRaw::new(&mut self.file, 0) - .unwrap(); // cannot fail with size 0 + let mut sg_raw = SgRaw::new(&mut self.file, 0).unwrap(); // cannot fail with size 0 sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); - cmd.push(0x0A); // WRITE + cmd.push(0x0A); // WRITE cmd.push(0x00); // VARIABLE SIZED BLOCKS cmd.push(((transfer_len >> 16) & 0xff) as u8); cmd.push(((transfer_len >> 8) & 0xff) as u8); @@ -649,8 +642,12 @@ impl SgTape { //println!("WRITE {:?}", data); match sg_raw.do_out_command(&cmd, data) { - Ok(()) => { Ok(false) } - Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 2 })) => { + Ok(()) => Ok(false), + Err(ScsiError::Sense(SenseInfo { + sense_key: 0, + asc: 0, + ascq: 2, + })) => { Ok(true) // LEOM } Err(err) => { @@ -663,19 +660,18 @@ impl SgTape { let transfer_len = buffer.len(); if transfer_len > 0xFFFFFF { - return Err(BlockReadError::Error( - proxmox_lang::io_format_err!("read failed - buffer too large") - )); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "read failed - buffer too large" + ))); } - let mut sg_raw = SgRaw::new(&mut self.file, 0) - .unwrap(); // cannot fail with size 0 + let mut sg_raw = SgRaw::new(&mut self.file, 0).unwrap(); // cannot fail with size 0 sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT); let mut cmd = Vec::new(); cmd.push(0x08); // READ cmd.push(0x02); // VARIABLE SIZED BLOCKS, SILI=1 - //cmd.push(0x00); // VARIABLE SIZED BLOCKS, SILI=0 + //cmd.push(0x00); // VARIABLE SIZED BLOCKS, SILI=0 cmd.push(((transfer_len >> 16) & 0xff) as u8); cmd.push(((transfer_len >> 8) & 0xff) as u8); cmd.push((transfer_len & 0xff) as u8); @@ -683,23 +679,34 @@ impl SgTape { let data = match sg_raw.do_in_command(&cmd, buffer) { Ok(data) => data, - Err(ScsiError::Sense(SenseInfo { sense_key: 0, asc: 0, ascq: 1 })) => { + Err(ScsiError::Sense(SenseInfo { + sense_key: 0, + asc: 0, + ascq: 1, + })) => { return Err(BlockReadError::EndOfFile); } - Err(ScsiError::Sense(SenseInfo { sense_key: 8, asc: 0, ascq: 5 })) => { + Err(ScsiError::Sense(SenseInfo { + sense_key: 8, + asc: 0, + ascq: 5, + })) => { return Err(BlockReadError::EndOfStream); } Err(err) => { - return Err(BlockReadError::Error( - proxmox_lang::io_format_err!("read failed - {}", err) - )); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "read failed - {}", + err + ))); } }; if data.len() != transfer_len { - return Err(BlockReadError::Error( - proxmox_lang::io_format_err!("read failed - unexpected block len ({} != {})", data.len(), buffer.len()) - )); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "read failed - unexpected block len ({} != {})", + data.len(), + buffer.len() + ))); } Ok(transfer_len) @@ -717,7 +724,6 @@ impl SgTape { /// Set all options we need/want pub fn set_default_options(&mut self) -> Result<(), Error> { - let compression = Some(true); let block_length = Some(0); // variable length mode let buffer_mode = Some(true); // Always use drive buffer @@ -734,7 +740,6 @@ impl SgTape { block_length: Option, buffer_mode: Option, ) -> Result<(), Error> { - // Note: Read/Modify/Write let (mut head, mut block_descriptor, mut page) = self.read_compression_page()?; @@ -766,7 +771,7 @@ impl SgTape { let mut cmd = Vec::new(); cmd.push(0x55); // MODE SELECT(10) cmd.push(0b0001_0000); // PF=1 - cmd.extend(&[0,0,0,0,0]); //reserved + cmd.extend(&[0, 0, 0, 0, 0]); //reserved let param_list_len: u16 = data.len() as u16; cmd.extend(¶m_list_len.to_be_bytes()); @@ -776,7 +781,8 @@ impl SgTape { buffer[..data.len()].copy_from_slice(&data[..]); - sg_raw.do_out_command(&cmd, &buffer[..data.len()]) + sg_raw + .do_out_command(&cmd, &buffer[..data.len()]) .map_err(|err| format_err!("set drive options failed - {}", err))?; Ok(()) @@ -784,10 +790,16 @@ impl SgTape { fn read_medium_configuration_page( &mut self, - ) -> Result<(ModeParameterHeader, ModeBlockDescriptor, MediumConfigurationModePage), Error> { - - let (head, block_descriptor, page): (_,_, MediumConfigurationModePage) - = scsi_mode_sense(&mut self.file, false, 0x1d, 0)?; + ) -> Result< + ( + ModeParameterHeader, + ModeBlockDescriptor, + MediumConfigurationModePage, + ), + Error, + > { + let (head, block_descriptor, page): (_, _, MediumConfigurationModePage) = + scsi_mode_sense(&mut self.file, false, 0x1d, 0)?; proxmox_lang::try_block!({ if (page.page_code & 0b0011_1111) != 0x1d { @@ -803,15 +815,22 @@ impl SgTape { }; Ok((head, block_descriptor, page)) - }).map_err(|err| format_err!("read_medium_configuration failed - {}", err)) + }) + .map_err(|err| format_err!("read_medium_configuration failed - {}", err)) } fn read_compression_page( &mut self, - ) -> Result<(ModeParameterHeader, ModeBlockDescriptor, DataCompressionModePage), Error> { - - let (head, block_descriptor, page): (_,_, DataCompressionModePage) - = scsi_mode_sense(&mut self.file, false, 0x0f, 0)?; + ) -> Result< + ( + ModeParameterHeader, + ModeBlockDescriptor, + DataCompressionModePage, + ), + Error, + > { + let (head, block_descriptor, page): (_, _, DataCompressionModePage) = + scsi_mode_sense(&mut self.file, false, 0x0f, 0)?; proxmox_lang::try_block!({ if (page.page_code & 0b0011_1111) != 0x0f { @@ -827,7 +846,8 @@ impl SgTape { }; Ok((head, block_descriptor, page)) - }).map_err(|err| format_err!("read_compression_page failed: {}", err)) + }) + .map_err(|err| format_err!("read_compression_page failed: {}", err)) } /// Read drive options/status @@ -835,7 +855,6 @@ impl SgTape { /// We read the drive compression page, including the /// block_descriptor. This is all information we need for now. pub fn read_drive_status(&mut self) -> Result { - // We do a Request Sense, but ignore the result. // This clears deferred error or media changed events. let _ = scsi_request_sense(&mut self.file); @@ -852,11 +871,11 @@ impl SgTape { } /// Get Tape and Media status - pub fn get_drive_and_media_status(&mut self) -> Result { - + pub fn get_drive_and_media_status(&mut self) -> Result { let drive_status = self.read_drive_status()?; - let alert_flags = self.tape_alert_flags() + let alert_flags = self + .tape_alert_flags() .map(|flags| format!("{:?}", flags)) .ok(); @@ -881,7 +900,6 @@ impl SgTape { }; if self.test_unit_ready().is_ok() { - if drive_status.write_protect { status.write_protect = Some(drive_status.write_protect); } @@ -892,7 +910,6 @@ impl SgTape { status.block_number = Some(position.logical_object_number); if let Ok(mam) = self.cartridge_memory() { - let usage = mam_extract_media_usage(&mam)?; status.manufactured = Some(usage.manufactured); @@ -900,7 +917,6 @@ impl SgTape { status.bytes_written = Some(usage.bytes_written); if let Ok(volume_stats) = self.volume_statistics() { - let passes = std::cmp::max( volume_stats.beginning_of_medium_passes, volume_stats.middle_of_tape_passes, @@ -908,7 +924,7 @@ impl SgTape { // assume max. 16000 medium passes // see: https://en.wikipedia.org/wiki/Linear_Tape-Open - let wearout: f64 = (passes as f64)/16000.0_f64; + let wearout: f64 = (passes as f64) / 16000.0_f64; status.medium_passes = Some(passes); status.medium_wearout = Some(wearout); @@ -920,7 +936,6 @@ impl SgTape { Ok(status) } - } impl Drop for SgTape { @@ -932,31 +947,33 @@ impl Drop for SgTape { } } - pub struct SgTapeReader<'a> { sg_tape: &'a mut SgTape, end_of_file: bool, } -impl <'a> SgTapeReader<'a> { - +impl<'a> SgTapeReader<'a> { pub fn new(sg_tape: &'a mut SgTape) -> Self { - Self { sg_tape, end_of_file: false, } + Self { + sg_tape, + end_of_file: false, + } } } -impl <'a> BlockRead for SgTapeReader<'a> { - +impl<'a> BlockRead for SgTapeReader<'a> { fn read_block(&mut self, buffer: &mut [u8]) -> Result { if self.end_of_file { - return Err(BlockReadError::Error(proxmox_lang::io_format_err!("detected read after EOF!"))); + return Err(BlockReadError::Error(proxmox_lang::io_format_err!( + "detected read after EOF!" + ))); } match self.sg_tape.read_block(buffer) { Ok(usize) => Ok(usize), Err(BlockReadError::EndOfFile) => { self.end_of_file = true; Err(BlockReadError::EndOfFile) - }, + } Err(err) => Err(err), } } @@ -967,15 +984,16 @@ pub struct SgTapeWriter<'a> { _leom_sent: bool, } -impl <'a> SgTapeWriter<'a> { - +impl<'a> SgTapeWriter<'a> { pub fn new(sg_tape: &'a mut SgTape) -> Self { - Self { sg_tape, _leom_sent: false } + Self { + sg_tape, + _leom_sent: false, + } } } -impl <'a> BlockWrite for SgTapeWriter<'a> { - +impl<'a> BlockWrite for SgTapeWriter<'a> { fn write_block(&mut self, buffer: &[u8]) -> Result { self.sg_tape.write_block(buffer) } diff --git a/pbs-tape/src/sg_tape/encryption.rs b/pbs-tape/src/sg_tape/encryption.rs index b212a7d4e..bd841ba0a 100644 --- a/pbs-tape/src/sg_tape/encryption.rs +++ b/pbs-tape/src/sg_tape/encryption.rs @@ -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( - file: &mut F, -) -> bool { - +pub fn has_encryption(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( /// Set or clear encryption key /// /// We always use mixed mode, -pub fn set_encryption( - file: &mut F, - key: Option<[u8; 32]>, -) -> Result<(), Error> { - +pub fn set_encryption(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( 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::(); @@ -106,7 +98,11 @@ fn sg_spout_set_encryption( 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( 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(file: &mut F) -> Result, 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(file: &mut F) -> Result, 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 { - 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 { 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 { 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 { - 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 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)) } diff --git a/pbs-tape/src/sg_tape/mam.rs b/pbs-tape/src/sg_tape/mam.rs index 441db4e11..4c9c5956a 100644 --- a/pbs-tape/src/sg_tape/mam.rs +++ b/pbs-tape/src/sg_tape/mam.rs @@ -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 = { let mut map = HashMap::new(); @@ -98,8 +177,7 @@ lazy_static::lazy_static!{ } fn read_tape_mam(file: &mut F) -> Result, 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(file: &mut F) -> Result, 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(file: &mut F) -> Result, Error> { - let data = read_tape_mam(file)?; decode_mam_attributes(&data) } fn decode_mam_attributes(data: &[u8]) -> Result, 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, 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, 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, Error> { } else { unreachable!(); } - }, + } MamFormat::BINARY => hex::encode(&data), }; list.push(MamAttribute { @@ -183,7 +264,10 @@ fn decode_mam_attributes(data: &[u8]) -> Result, 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 { - - 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 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::()? * 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::()? * 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::()? * 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::()? * 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, + }) } diff --git a/pbs-tape/src/sg_tape/report_density.rs b/pbs-tape/src/sg_tape/report_density.rs index f63d6bb25..51f563edb 100644 --- a/pbs-tape/src/sg_tape/report_density.rs +++ b/pbs-tape/src/sg_tape/report_density.rs @@ -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(file: &mut F) -> Result { 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(file: &mut F) -> Result { 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(file: &mut F) -> Result { } Ok(()) - - }).map_err(|err| format_err!("decode report density failed - {}", err))?; + }) + .map_err(|err| format_err!("decode report density failed - {}", err))?; Ok(max_density) } diff --git a/pbs-tape/src/sg_tape/tape_alert_flags.rs b/pbs-tape/src/sg_tape/tape_alert_flags.rs index afd26285c..8b25d4bdb 100644 --- a/pbs-tape/src/sg_tape/tape_alert_flags.rs +++ b/pbs-tape/src/sg_tape/tape_alert_flags.rs @@ -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(file: &mut F) -> Result { - +pub fn read_tape_alert_flags(file: &mut F) -> Result { let data = sg_read_tape_alert_flags(file)?; decode_tape_alert_flags(&data) } - fn sg_read_tape_alert_flags(file: &mut F) -> Result, 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(file: &mut F) -> Result, 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(file: &mut F) -> Result, 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 { - 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 { 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 { diff --git a/pbs-tape/src/sg_tape/volume_statistics.rs b/pbs-tape/src/sg_tape/volume_statistics.rs index e8790e3f8..3bacfdc4e 100644 --- a/pbs-tape/src/sg_tape/volume_statistics.rs +++ b/pbs-tape/src/sg_tape/volume_statistics.rs @@ -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(file: &mut F) -> Result { - +pub fn read_volume_statistics(file: &mut F) -> Result { let data = sg_read_volume_statistics(file)?; decode_volume_statistics(&data) } fn sg_read_volume_statistics(file: &mut F) -> Result, 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(file: &mut F) -> Result, 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 { - - 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 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 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 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 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 } } 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 } Ok(stat) - - }).map_err(|err| format_err!("decode volume statistics failed - {}", err)) + }) + .map_err(|err| format_err!("decode volume statistics failed - {}", err)) } diff --git a/pbs-tape/src/sgutils2.rs b/pbs-tape/src/sgutils2.rs index dbef184a7..1fc487bee 100644 --- a/pbs-tape/src/sgutils2.rs +++ b/pbs-tape/src/sgutils2.rs @@ -25,9 +25,7 @@ pub struct SenseInfo { } impl std::fmt::Display for SenseInfo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let sense_text = SENSE_KEY_DESCRIPTIONS .get(self.sense_key as usize) .map(|s| String::from(*s)) @@ -58,7 +56,9 @@ impl From for ScsiError { // Opaque wrapper for sg_pt_base #[repr(C)] -struct SgPtBase { _private: [u8; 0] } +struct SgPtBase { + _private: [u8; 0], +} #[repr(transparent)] struct SgPt { @@ -97,7 +97,7 @@ pub const PERIPHERAL_DEVICE_TYPE_TEXT: [&str; 32] = [ "Printer", "Processor", "Write-once", - "CD-ROM", // 05h + "CD-ROM", // 05h "Scanner", "Optical", "Medium Changer", // 08h @@ -127,19 +127,19 @@ pub const PERIPHERAL_DEVICE_TYPE_TEXT: [&str; 32] = [ ]; // SENSE KEYS -pub const SENSE_KEY_NO_SENSE: u8 = 0x00; +pub const SENSE_KEY_NO_SENSE: u8 = 0x00; pub const SENSE_KEY_RECOVERED_ERROR: u8 = 0x01; -pub const SENSE_KEY_NOT_READY: u8 = 0x02; -pub const SENSE_KEY_MEDIUM_ERROR: u8 = 0x03; -pub const SENSE_KEY_HARDWARE_ERROR: u8 = 0x04; +pub const SENSE_KEY_NOT_READY: u8 = 0x02; +pub const SENSE_KEY_MEDIUM_ERROR: u8 = 0x03; +pub const SENSE_KEY_HARDWARE_ERROR: u8 = 0x04; pub const SENSE_KEY_ILLEGAL_REQUEST: u8 = 0x05; -pub const SENSE_KEY_UNIT_ATTENTION: u8 = 0x06; -pub const SENSE_KEY_DATA_PROTECT: u8 = 0x07; -pub const SENSE_KEY_BLANK_CHECK: u8 = 0x08; -pub const SENSE_KEY_COPY_ABORTED: u8 = 0x0a; +pub const SENSE_KEY_UNIT_ATTENTION: u8 = 0x06; +pub const SENSE_KEY_DATA_PROTECT: u8 = 0x07; +pub const SENSE_KEY_BLANK_CHECK: u8 = 0x08; +pub const SENSE_KEY_COPY_ABORTED: u8 = 0x0a; pub const SENSE_KEY_ABORTED_COMMAND: u8 = 0x0b; pub const SENSE_KEY_VOLUME_OVERFLOW: u8 = 0x0d; -pub const SENSE_KEY_MISCOMPARE: u8 = 0x0e; +pub const SENSE_KEY_MISCOMPARE: u8 = 0x0e; /// Sense Key Descriptions pub const SENSE_KEY_DESCRIPTIONS: [&str; 16] = [ @@ -184,9 +184,9 @@ pub struct RequestSenseFixed { pub response_code: u8, obsolete: u8, pub flags2: u8, - pub information: [u8;4], + pub information: [u8; 4], pub additional_sense_len: u8, - pub command_specific_information: [u8;4], + pub command_specific_information: [u8; 4], pub additional_sense_code: u8, pub additional_sense_code_qualifier: u8, pub field_replacable_unit_code: u8, @@ -195,12 +195,12 @@ pub struct RequestSenseFixed { #[repr(C, packed)] #[derive(Endian, Debug)] -struct RequestSenseDescriptor{ +struct RequestSenseDescriptor { response_code: u8, sense_key: u8, additional_sense_code: u8, additional_sense_code_qualifier: u8, - reserved: [u8;4], + reserved: [u8; 4], additional_sense_len: u8, } @@ -228,12 +228,11 @@ pub struct ModeParameterHeader { // not compatible with IBM. pub medium_type: u8, pub flags3: u8, - reserved4: [u8;2], + reserved4: [u8; 2], pub block_descriptior_len: u16, } impl ModeParameterHeader { - pub fn buffer_mode(&self) -> u8 { (self.flags3 & 0b0111_0000) >> 4 } @@ -256,18 +255,16 @@ impl ModeParameterHeader { /// SCSI ModeBlockDescriptor for Tape devices pub struct ModeBlockDescriptor { pub density_code: u8, - pub number_of_blocks: [u8;3], + pub number_of_blocks: [u8; 3], reserverd: u8, pub block_length: [u8; 3], } impl ModeBlockDescriptor { - pub fn block_length(&self) -> u32 { - ((self.block_length[0] as u32) << 16) + - ((self.block_length[1] as u32) << 8) + - (self.block_length[2] as u32) - + ((self.block_length[0] as u32) << 16) + + ((self.block_length[1] as u32) << 8) + + (self.block_length[2] as u32) } pub fn set_block_length(&mut self, length: u32) -> Result<(), Error> { @@ -281,64 +278,36 @@ impl ModeBlockDescriptor { } } -pub const SCSI_PT_DO_START_OK:c_int = 0; -pub const SCSI_PT_DO_BAD_PARAMS:c_int = 1; -pub const SCSI_PT_DO_TIMEOUT:c_int = 2; +pub const SCSI_PT_DO_START_OK: c_int = 0; +pub const SCSI_PT_DO_BAD_PARAMS: c_int = 1; +pub const SCSI_PT_DO_TIMEOUT: c_int = 2; -pub const SCSI_PT_RESULT_GOOD:c_int = 0; -pub const SCSI_PT_RESULT_STATUS:c_int = 1; -pub const SCSI_PT_RESULT_SENSE:c_int = 2; -pub const SCSI_PT_RESULT_TRANSPORT_ERR:c_int = 3; -pub const SCSI_PT_RESULT_OS_ERR:c_int = 4; +pub const SCSI_PT_RESULT_GOOD: c_int = 0; +pub const SCSI_PT_RESULT_STATUS: c_int = 1; +pub const SCSI_PT_RESULT_SENSE: c_int = 2; +pub const SCSI_PT_RESULT_TRANSPORT_ERR: c_int = 3; +pub const SCSI_PT_RESULT_OS_ERR: c_int = 4; #[link(name = "sgutils2")] extern "C" { #[allow(dead_code)] - fn scsi_pt_open_device( - device_name: * const c_char, - read_only: bool, - verbose: c_int, - ) -> c_int; + fn scsi_pt_open_device(device_name: *const c_char, read_only: bool, verbose: c_int) -> c_int; - fn sg_is_scsi_cdb( - cdbp: *const u8, - clen: c_int, - ) -> bool; + fn sg_is_scsi_cdb(cdbp: *const u8, clen: c_int) -> bool; fn construct_scsi_pt_obj() -> *mut SgPtBase; fn destruct_scsi_pt_obj(objp: *mut SgPtBase); - fn set_scsi_pt_data_in( - objp: *mut SgPtBase, - dxferp: *mut u8, - dxfer_ilen: c_int, - ); + fn set_scsi_pt_data_in(objp: *mut SgPtBase, dxferp: *mut u8, dxfer_ilen: c_int); - fn set_scsi_pt_data_out( - objp: *mut SgPtBase, - dxferp: *const u8, - dxfer_olen: c_int, - ); + fn set_scsi_pt_data_out(objp: *mut SgPtBase, dxferp: *const u8, dxfer_olen: c_int); - fn set_scsi_pt_cdb( - objp: *mut SgPtBase, - cdb: *const u8, - cdb_len: c_int, - ); + fn set_scsi_pt_cdb(objp: *mut SgPtBase, cdb: *const u8, cdb_len: c_int); - fn set_scsi_pt_sense( - objp: *mut SgPtBase, - sense: *mut u8, - max_sense_len: c_int, - ); + fn set_scsi_pt_sense(objp: *mut SgPtBase, sense: *mut u8, max_sense_len: c_int); - fn do_scsi_pt( - objp: *mut SgPtBase, - fd: c_int, - timeout_secs: c_int, - verbose: c_int, - ) -> c_int; + fn do_scsi_pt(objp: *mut SgPtBase, fd: c_int, timeout_secs: c_int, verbose: c_int) -> c_int; fn get_scsi_pt_resid(objp: *const SgPtBase) -> c_int; @@ -352,10 +321,10 @@ extern "C" { fn sg_get_asc_ascq_str( asc: c_int, - ascq:c_int, + ascq: c_int, buff_len: c_int, buffer: *mut c_char, - ) -> * const c_char; + ) -> *const c_char; } /// Safe interface to run RAW SCSI commands @@ -368,31 +337,30 @@ pub struct SgRaw<'a, F> { /// Get the string associated with ASC/ASCQ values pub fn get_asc_ascq_string(asc: u8, ascq: u8) -> String { - let mut buffer = [0u8; 1024]; let res = unsafe { sg_get_asc_ascq_str( asc as c_int, ascq as c_int, buffer.len() as c_int, - buffer.as_mut_ptr() as * mut c_char, + buffer.as_mut_ptr() as *mut c_char, ) }; proxmox_lang::try_block!({ - if res.is_null() { // just to be safe + if res.is_null() { + // just to be safe bail!("unexpected NULL ptr"); } Ok(unsafe { CStr::from_ptr(res) }.to_str()?.to_owned()) - }).unwrap_or_else(|_err: Error| { - format!("ASC={:02x}x, ASCQ={:02x}x", asc, ascq) }) + .unwrap_or_else(|_err: Error| format!("ASC={:02x}x, ASCQ={:02x}x", asc, ascq)) } /// Allocate a page aligned buffer /// /// SG RAWIO commands needs page aligned transfer buffers. -pub fn alloc_page_aligned_buffer(buffer_size: usize) -> Result , Error> { +pub fn alloc_page_aligned_buffer(buffer_size: usize) -> Result, Error> { let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; let layout = std::alloc::Layout::from_size_align(buffer_size, page_size)?; let dinp = unsafe { std::alloc::alloc_zeroed(layout) }; @@ -400,28 +368,31 @@ pub fn alloc_page_aligned_buffer(buffer_size: usize) -> Result , Error bail!("alloc SCSI output buffer failed"); } - let buffer_slice = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)}; + let buffer_slice = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size) }; Ok(unsafe { Box::from_raw(buffer_slice) }) } -impl <'a, F: AsRawFd> SgRaw<'a, F> { - +impl<'a, F: AsRawFd> SgRaw<'a, F> { /// Create a new instance to run commands /// /// The file must be a handle to a SCSI device. pub fn new(file: &'a mut F, buffer_size: usize) -> Result { - let buffer; if buffer_size > 0 { buffer = alloc_page_aligned_buffer(buffer_size)?; } else { - buffer = Box::new([]); + buffer = Box::new([]); } let sense_buffer = [0u8; 32]; - Ok(Self { file, buffer, sense_buffer, timeout: 0 }) + Ok(Self { + file, + buffer, + sense_buffer, + timeout: 0, + }) } /// Set the command timeout in seconds (0 means default (60 seconds)) @@ -435,7 +406,6 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { // create new object with initialized data_in and sense buffer fn create_scsi_pt_obj(&mut self) -> Result { - let mut ptvp = SgPt::new()?; if !self.buffer.is_empty() { @@ -460,18 +430,21 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { } fn do_scsi_pt_checked(&mut self, ptvp: &mut SgPt) -> Result<(), ScsiError> { - let res = unsafe { do_scsi_pt(ptvp.as_mut_ptr(), self.file.as_raw_fd(), self.timeout, 0) }; match res { - SCSI_PT_DO_START_OK => { /* Ok */ }, - SCSI_PT_DO_BAD_PARAMS => return Err(format_err!("do_scsi_pt failed - bad pass through setup").into()), + SCSI_PT_DO_START_OK => { /* Ok */ } + SCSI_PT_DO_BAD_PARAMS => { + return Err(format_err!("do_scsi_pt failed - bad pass through setup").into()) + } SCSI_PT_DO_TIMEOUT => return Err(format_err!("do_scsi_pt failed - timeout").into()), code if code < 0 => { let errno = unsafe { get_scsi_pt_os_err(ptvp.as_ptr()) }; let err = nix::Error::from_errno(nix::errno::Errno::from_i32(errno)); return Err(format_err!("do_scsi_pt failed with err {}", err).into()); } - unknown => return Err(format_err!("do_scsi_pt failed: unknown error {}", unknown).into()), + unknown => { + return Err(format_err!("do_scsi_pt failed: unknown error {}", unknown).into()) + } } if res < 0 { @@ -490,7 +463,9 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { SCSI_PT_RESULT_STATUS => { let status = unsafe { get_scsi_pt_status_response(ptvp.as_ptr()) }; if status != 0 { - return Err(format_err!("unknown scsi error - status response {}", status).into()); + return Err( + format_err!("unknown scsi error - status response {}", status).into(), + ); } Ok(()) } @@ -521,28 +496,39 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { } } 0x71 | 0x73 => { - return Err(format_err!("scsi command failed: received deferred Sense").into()); + return Err( + format_err!("scsi command failed: received deferred Sense").into() + ); } unknown => { - return Err(format_err!("scsi command failed: invalid Sense response code {:x}", unknown).into()); + return Err(format_err!( + "scsi command failed: invalid Sense response code {:x}", + unknown + ) + .into()); } }; Err(ScsiError::Sense(sense)) } - SCSI_PT_RESULT_TRANSPORT_ERR => return Err(format_err!("scsi command failed: transport error").into()), + SCSI_PT_RESULT_TRANSPORT_ERR => { + return Err(format_err!("scsi command failed: transport error").into()) + } SCSI_PT_RESULT_OS_ERR => { let errno = unsafe { get_scsi_pt_os_err(ptvp.as_ptr()) }; let err = nix::Error::from_errno(nix::errno::Errno::from_i32(errno)); return Err(format_err!("scsi command failed with err {}", err).into()); } - unknown => return Err(format_err!("scsi command failed: unknown result category {}", unknown).into()), + unknown => { + return Err( + format_err!("scsi command failed: unknown result category {}", unknown).into(), + ) + } } } /// Run the specified RAW SCSI command pub fn do_command(&mut self, cmd: &[u8]) -> Result<&[u8], ScsiError> { - if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) } { return Err(format_err!("no valid SCSI command").into()); } @@ -553,19 +539,15 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { let mut ptvp = self.create_scsi_pt_obj()?; - unsafe { - set_scsi_pt_cdb( - ptvp.as_mut_ptr(), - cmd.as_ptr(), - cmd.len() as c_int, - ) - }; + unsafe { set_scsi_pt_cdb(ptvp.as_mut_ptr(), cmd.as_ptr(), cmd.len() as c_int) }; self.do_scsi_pt_checked(&mut ptvp)?; let resid = unsafe { get_scsi_pt_resid(ptvp.as_ptr()) } as usize; if resid > self.buffer.len() { - return Err(format_err!("do_scsi_pt failed - got strange resid (value too big)").into()); + return Err( + format_err!("do_scsi_pt failed - got strange resid (value too big)").into(), + ); } let data_len = self.buffer.len() - resid; @@ -573,8 +555,11 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { } /// Run the specified RAW SCSI command, use data as input buffer - pub fn do_in_command<'b>(&mut self, cmd: &[u8], data: &'b mut [u8]) -> Result<&'b [u8], ScsiError> { - + pub fn do_in_command<'b>( + &mut self, + cmd: &[u8], + data: &'b mut [u8], + ) -> Result<&'b [u8], ScsiError> { if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) } { return Err(format_err!("no valid SCSI command").into()); } @@ -586,17 +571,9 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { let mut ptvp = self.create_scsi_pt_obj()?; unsafe { - set_scsi_pt_data_in( - ptvp.as_mut_ptr(), - data.as_mut_ptr(), - data.len() as c_int, - ); + set_scsi_pt_data_in(ptvp.as_mut_ptr(), data.as_mut_ptr(), data.len() as c_int); - set_scsi_pt_cdb( - ptvp.as_mut_ptr(), - cmd.as_ptr(), - cmd.len() as c_int, - ); + set_scsi_pt_cdb(ptvp.as_mut_ptr(), cmd.as_ptr(), cmd.len() as c_int); }; self.do_scsi_pt_checked(&mut ptvp)?; @@ -604,7 +581,9 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { let resid = unsafe { get_scsi_pt_resid(ptvp.as_ptr()) } as usize; if resid > data.len() { - return Err(format_err!("do_scsi_pt failed - got strange resid (value too big)").into()); + return Err( + format_err!("do_scsi_pt failed - got strange resid (value too big)").into(), + ); } let data_len = data.len() - resid; @@ -615,30 +594,21 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> { /// /// Note: use alloc_page_aligned_buffer to alloc data transfer buffer pub fn do_out_command(&mut self, cmd: &[u8], data: &[u8]) -> Result<(), ScsiError> { - if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) } { return Err(format_err!("no valid SCSI command").into()); } let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize; - if ((data.as_ptr() as usize) & (page_size -1)) != 0 { + if ((data.as_ptr() as usize) & (page_size - 1)) != 0 { return Err(format_err!("wrong transfer buffer alignment").into()); } let mut ptvp = self.create_scsi_pt_obj()?; unsafe { - set_scsi_pt_data_out( - ptvp.as_mut_ptr(), - data.as_ptr(), - data.len() as c_int, - ); + set_scsi_pt_data_out(ptvp.as_mut_ptr(), data.as_ptr(), data.len() as c_int); - set_scsi_pt_cdb( - ptvp.as_mut_ptr(), - cmd.as_ptr(), - cmd.len() as c_int, - ); + set_scsi_pt_cdb(ptvp.as_mut_ptr(), cmd.as_ptr(), cmd.len() as c_int); }; self.do_scsi_pt_checked(&mut ptvp)?; @@ -660,10 +630,7 @@ pub fn scsi_ascii_to_string(data: &[u8]) -> String { /// Read SCSI Inquiry page /// /// Returns Product/Vendor/Revision and device type. -pub fn scsi_inquiry( - file: &mut F, -) -> Result { - +pub fn scsi_inquiry(file: &mut F) -> Result { let allocation_len: u8 = std::mem::size_of::() as u8; let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; @@ -672,13 +639,14 @@ pub fn scsi_inquiry( let mut cmd = Vec::new(); cmd.extend(&[0x12, 0, 0, 0, allocation_len, 0]); // INQUIRY - let data = sg_raw.do_command(&cmd) + let data = sg_raw + .do_command(&cmd) .map_err(|err| format_err!("SCSI inquiry failed - {}", err))?; proxmox_lang::try_block!({ let mut reader = data; - let page: InquiryPage = unsafe { reader.read_be_value()? }; + let page: InquiryPage = unsafe { reader.read_be_value()? }; let peripheral_type = page.peripheral_type & 31; @@ -691,7 +659,8 @@ pub fn scsi_inquiry( }; Ok(info) - }).map_err(|err: Error| format_err!("decode inquiry page failed - {}", err)) + }) + .map_err(|err: Error| format_err!("decode inquiry page failed - {}", err)) } /// Run SCSI Mode Sense @@ -703,7 +672,6 @@ pub fn scsi_mode_sense( page_code: u8, sub_page_code: u8, ) -> Result<(ModeParameterHeader, Option, P), Error> { - let allocation_len: u16 = 4096; let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; @@ -721,7 +689,8 @@ pub fn scsi_mode_sense( cmd.extend(&allocation_len.to_be_bytes()); // allocation len cmd.push(0); //control - let data = sg_raw.do_command(&cmd) + let data = sg_raw + .do_command(&cmd) .map_err(|err| format_err!("mode sense failed - {}", err))?; proxmox_lang::try_block!({ @@ -731,7 +700,11 @@ pub fn scsi_mode_sense( let expected_len = head.mode_data_len as usize + 2; if data.len() < expected_len { - bail!("wrong mode_data_len: got {}, expected {}", data.len(), expected_len); + bail!( + "wrong mode_data_len: got {}, expected {}", + data.len(), + expected_len + ); } else if data.len() > expected_len { // Note: Some hh7 drives returns the allocation_length // instead of real data_len @@ -758,23 +731,22 @@ pub fn scsi_mode_sense( let page: P = unsafe { reader.read_be_value()? }; Ok((head, block_descriptor, page)) - }).map_err(|err: Error| format_err!("decode mode sense failed - {}", err)) + }) + .map_err(|err: Error| format_err!("decode mode sense failed - {}", err)) } /// Resuqest Sense -pub fn scsi_request_sense( - file: &mut F, -) -> Result { - +pub fn scsi_request_sense(file: &mut F) -> Result { // request 252 bytes, as mentioned in the Seagate SCSI reference let allocation_len: u8 = 252; - let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; + let mut sg_raw = SgRaw::new(file, allocation_len as usize)?; sg_raw.set_timeout(30); // use short timeout let mut cmd = Vec::new(); cmd.extend(&[0x03, 0, 0, 0, allocation_len, 0]); // REQUEST SENSE FIXED FORMAT - let data = sg_raw.do_command(&cmd) + let data = sg_raw + .do_command(&cmd) .map_err(|err| format_err!("request sense failed - {}", err))?; let sense = proxmox_lang::try_block!({ @@ -793,7 +765,8 @@ pub fn scsi_request_sense( let sense: RequestSenseFixed = unsafe { reader.read_be_value()? }; Ok(sense) - }).map_err(|err: Error| format_err!("decode request sense failed - {}", err))?; + }) + .map_err(|err: Error| format_err!("decode request sense failed - {}", err))?; Ok(sense) } diff --git a/pbs-tape/src/tape_write.rs b/pbs-tape/src/tape_write.rs index 0d52e71da..2808516d6 100644 --- a/pbs-tape/src/tape_write.rs +++ b/pbs-tape/src/tape_write.rs @@ -38,12 +38,16 @@ pub trait TapeWrite { } let header = header.to_le(); - let res = self.write_all(unsafe { std::slice::from_raw_parts( - &header as *const MediaContentHeader as *const u8, - std::mem::size_of::(), - )})?; + let res = self.write_all(unsafe { + std::slice::from_raw_parts( + &header as *const MediaContentHeader as *const u8, + std::mem::size_of::(), + ) + })?; - if data.is_empty() { return Ok(res); } + if data.is_empty() { + return Ok(res); + } self.write_all(data) }