tape: correctly set/display drive option

This commit is contained in:
Dietmar Maurer 2021-04-07 11:11:35 +02:00
parent b717871d2a
commit 0892a512bc
4 changed files with 182 additions and 36 deletions

View File

@ -176,13 +176,15 @@ impl TryFrom<u8> for TapeDensity {
pub struct LtoDriveAndMediaStatus { pub struct LtoDriveAndMediaStatus {
/// Block size (0 is variable size) /// Block size (0 is variable size)
pub blocksize: u32, pub blocksize: u32,
/// Compression enabled
pub compression: bool,
/// Drive buffer mode
pub buffer_mode: u8,
/// Tape density /// Tape density
pub density: TapeDensity,
/// Media is write protected
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub density: Option<TapeDensity>, pub write_protect: Option<bool>,
/// Status flags
pub status: String,
/// Lto Driver Options
pub options: String,
/// Tape Alert Flags /// Tape Alert Flags
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub alert_flags: Option<String>, pub alert_flags: Option<String>,

View File

@ -741,8 +741,9 @@ async fn status(mut param: Value) -> Result<(), Error> {
let options = default_table_format_options() let options = default_table_format_options()
.column(ColumnConfig::new("blocksize")) .column(ColumnConfig::new("blocksize"))
.column(ColumnConfig::new("density")) .column(ColumnConfig::new("density"))
.column(ColumnConfig::new("status")) .column(ColumnConfig::new("compression"))
.column(ColumnConfig::new("options")) .column(ColumnConfig::new("buffer-mode"))
.column(ColumnConfig::new("write-protect"))
.column(ColumnConfig::new("alert-flags")) .column(ColumnConfig::new("alert-flags"))
.column(ColumnConfig::new("file-number")) .column(ColumnConfig::new("file-number"))
.column(ColumnConfig::new("block-number")) .column(ColumnConfig::new("block-number"))

View File

@ -38,6 +38,7 @@ use crate::{
MamAttribute, MamAttribute,
LtoDriveAndMediaStatus, LtoDriveAndMediaStatus,
LtoTapeDrive, LtoTapeDrive,
TapeDensity,
}, },
tape::{ tape::{
TapeRead, TapeRead,
@ -82,8 +83,7 @@ impl LtoTapeDrive {
handle.sg_tape.wait_until_ready()?; handle.sg_tape.wait_until_ready()?;
// Only root can set driver options, so we cannot handle.set_default_options()?;
// handle.set_default_options()?;
Ok(handle) Ok(handle)
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", self.name, self.path, err)) }).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", self.name, self.path, err))
@ -104,8 +104,14 @@ impl LtoTapeHandle {
} }
/// Set all options we need/want /// Set all options we need/want
pub fn set_default_options(&self) -> Result<(), Error> { pub fn set_default_options(&mut self) -> Result<(), Error> {
// fixme
let compression = Some(true);
let block_length = Some(0); // variable length mode
let buffer_mode = Some(true); // Always use drive buffer
self.sg_tape.set_drive_options(compression, block_length, buffer_mode)?;
Ok(()) Ok(())
} }
@ -117,28 +123,21 @@ impl LtoTapeHandle {
/// Get Tape and Media status /// Get Tape and Media status
pub fn get_drive_and_media_status(&mut self) -> Result<LtoDriveAndMediaStatus, Error> { pub fn get_drive_and_media_status(&mut self) -> Result<LtoDriveAndMediaStatus, Error> {
let (file_number, block_number) = match self.sg_tape.position() { let drive_status = self.sg_tape.read_drive_status()?;
Ok(position) => (
Some(position.logical_file_id),
Some(position.logical_object_number),
),
Err(_) => (None, None),
};
let options = String::from("FIXME");
let alert_flags = self.tape_alert_flags() let alert_flags = self.tape_alert_flags()
.map(|flags| format!("{:?}", flags)) .map(|flags| format!("{:?}", flags))
.ok(); .ok();
let mut status = LtoDriveAndMediaStatus { let mut status = LtoDriveAndMediaStatus {
blocksize: 0, // fixme: remove blocksize: drive_status.block_length,
density: None, // fixme compression: drive_status.compression,
status: String::from("FIXME"), buffer_mode: drive_status.buffer_mode,
options, density: TapeDensity::try_from(drive_status.density_code)?,
alert_flags, alert_flags,
file_number, write_protect: None,
block_number, file_number: None,
block_number: None,
manufactured: None, manufactured: None,
bytes_read: None, bytes_read: None,
bytes_written: None, bytes_written: None,
@ -147,7 +146,16 @@ impl LtoTapeHandle {
volume_mounts: None, volume_mounts: None,
}; };
if self.sg_tape.test_unit_ready()? { if self.sg_tape.test_unit_ready().is_ok() {
if drive_status.write_protect {
status.write_protect = Some(drive_status.write_protect);
}
let position = self.sg_tape.position()?;
status.file_number = Some(position.logical_file_id);
status.block_number = Some(position.logical_object_number);
if let Ok(mam) = self.cartridge_memory() { if let Ok(mam) = self.cartridge_memory() {

View File

@ -10,7 +10,7 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag};
use proxmox::{ use proxmox::{
sys::error::SysResult, sys::error::SysResult,
tools::io::ReadExt, tools::io::{ReadExt, WriteExt},
}; };
use crate::{ use crate::{
@ -39,7 +39,11 @@ use crate::{
SenseInfo, SenseInfo,
ScsiError, ScsiError,
InquiryInfo, InquiryInfo,
ModeParameterHeader,
ModeBlockDescriptor,
alloc_page_aligned_buffer,
scsi_inquiry, scsi_inquiry,
scsi_mode_sense,
}, },
}; };
@ -54,6 +58,42 @@ pub struct ReadPositionLongPage {
obsolete: [u8;8], obsolete: [u8;8],
} }
#[repr(C, packed)]
#[derive(Endian, Debug, Copy, Clone)]
struct DataCompressionModePage {
page_code: u8, // 0x0f
page_length: u8, // 0x0e
flags2: u8,
flags3: u8,
compression_algorithm: u32,
decompression_algorithm: u32,
reserved: [u8;4],
}
impl DataCompressionModePage {
pub fn set_compression(&mut self, enable: bool) {
if enable {
self.flags2 |= 128;
} else {
self.flags2 = self.flags2 & 127;
}
}
pub fn compression_enabled(&self) -> bool {
(self.flags2 & 0b1000_0000) != 0
}
}
#[derive(Debug)]
pub struct LtoTapeStatus {
pub block_length: u32,
pub density_code: u8,
pub buffer_mode: u8,
pub write_protect: bool,
pub compression: bool,
}
pub struct SgTape { pub struct SgTape {
file: File, file: File,
} }
@ -378,19 +418,19 @@ impl SgTape {
Ok(()) Ok(())
} }
pub fn test_unit_ready(&mut self) -> Result<bool, Error> { pub fn test_unit_ready(&mut self) -> Result<(), Error> {
let mut sg_raw = SgRaw::new(&mut self.file, 16)?; let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
sg_raw.set_timeout(30); // use short timeout sg_raw.set_timeout(30); // use short timeout
let mut cmd = Vec::new(); let mut cmd = Vec::new();
cmd.extend(&[0x00, 0, 0, 0, 0, 0]); // TEST UNIT READY cmd.extend(&[0x00, 0, 0, 0, 0, 0]); // TEST UNIT READY
// fixme: check sense match sg_raw.do_command(&cmd) {
sg_raw.do_command(&cmd) Ok(_) => Ok(()),
.map_err(|err| format_err!("unit not ready - {}", err))?; Err(err) => {
bail!("test_unit_ready failed - {}", err);
Ok(true) }
}
} }
pub fn wait_until_ready(&mut self) -> Result<(), Error> { pub fn wait_until_ready(&mut self) -> Result<(), Error> {
@ -400,7 +440,7 @@ impl SgTape {
loop { loop {
match self.test_unit_ready() { match self.test_unit_ready() {
Ok(true) => return Ok(()), Ok(()) => return Ok(()),
_ => { _ => {
std::thread::sleep(std::time::Duration::new(1, 0)); std::thread::sleep(std::time::Duration::new(1, 0));
if start.elapsed()? > max_wait { if start.elapsed()? > max_wait {
@ -522,6 +562,101 @@ impl SgTape {
None => Ok(None), None => Ok(None),
} }
} }
/// Set important drive options
pub fn set_drive_options(
&mut self,
compression: Option<bool>,
block_length: Option<u32>,
buffer_mode: Option<bool>,
) -> Result<(), Error> {
// Note: Read/Modify/Write
let (mut head, mut block_descriptor, mut page) = self.read_compression_page()?;
let mut sg_raw = SgRaw::new(&mut self.file, 0)?;
sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
head.mode_data_len = 0; // need to b e zero
if let Some(compression) = compression {
page.set_compression(compression);
}
if let Some(block_length) = block_length {
block_descriptor.set_block_length(block_length)?;
}
if let Some(buffer_mode) = buffer_mode {
let mut mode = head.flags3 & 0b1_000_1111;
if buffer_mode {
mode |= 0b0_001_0000;
}
head.flags3 = mode;
}
let mut data = Vec::new();
unsafe {
data.write_be_value(head)?;
data.write_be_value(block_descriptor)?;
data.write_be_value(page)?;
}
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
let param_list_len: u16 = data.len() as u16;
cmd.extend(&param_list_len.to_be_bytes());
cmd.push(0); // control
let mut buffer = alloc_page_aligned_buffer(4096)?;
buffer[..data.len()].copy_from_slice(&data[..]);
sg_raw.do_out_command(&cmd, &buffer[..data.len()])
.map_err(|err| format_err!("set drive options failed - {}", err))?;
Ok(())
}
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)?;
if !(page.page_code == 0x0f && page.page_length == 0x0e) {
bail!("read_compression_page: got strange page code/length");
}
let block_descriptor = match block_descriptor {
Some(block_descriptor) => block_descriptor,
None => bail!("read_compression_page failed: missing block descriptor"),
};
Ok((head, block_descriptor, page))
}
/// Read drive options/status
///
/// 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<LtoTapeStatus, Error> {
let (head, block_descriptor, page) = self.read_compression_page()?;
Ok(LtoTapeStatus {
block_length: block_descriptor.block_length(),
write_protect: (head.flags3 & 0b1000_0000) != 0,
buffer_mode: (head.flags3 & 0b0111_0000) >> 4,
compression: page.compression_enabled(),
density_code: block_descriptor.density_code,
})
}
} }
pub struct SgTapeReader<'a> { pub struct SgTapeReader<'a> {