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 {
/// Block size (0 is variable size)
pub blocksize: u32,
/// Compression enabled
pub compression: bool,
/// Drive buffer mode
pub buffer_mode: u8,
/// Tape density
pub density: TapeDensity,
/// Media is write protected
#[serde(skip_serializing_if="Option::is_none")]
pub density: Option<TapeDensity>,
/// Status flags
pub status: String,
/// Lto Driver Options
pub options: String,
pub write_protect: Option<bool>,
/// Tape Alert Flags
#[serde(skip_serializing_if="Option::is_none")]
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()
.column(ColumnConfig::new("blocksize"))
.column(ColumnConfig::new("density"))
.column(ColumnConfig::new("status"))
.column(ColumnConfig::new("options"))
.column(ColumnConfig::new("compression"))
.column(ColumnConfig::new("buffer-mode"))
.column(ColumnConfig::new("write-protect"))
.column(ColumnConfig::new("alert-flags"))
.column(ColumnConfig::new("file-number"))
.column(ColumnConfig::new("block-number"))

View File

@ -38,6 +38,7 @@ use crate::{
MamAttribute,
LtoDriveAndMediaStatus,
LtoTapeDrive,
TapeDensity,
},
tape::{
TapeRead,
@ -82,8 +83,7 @@ impl LtoTapeDrive {
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)
}).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
pub fn set_default_options(&self) -> Result<(), Error> {
// fixme
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
self.sg_tape.set_drive_options(compression, block_length, buffer_mode)?;
Ok(())
}
@ -117,28 +123,21 @@ impl LtoTapeHandle {
/// Get Tape and Media status
pub fn get_drive_and_media_status(&mut self) -> Result<LtoDriveAndMediaStatus, Error> {
let (file_number, block_number) = match self.sg_tape.position() {
Ok(position) => (
Some(position.logical_file_id),
Some(position.logical_object_number),
),
Err(_) => (None, None),
};
let options = String::from("FIXME");
let drive_status = self.sg_tape.read_drive_status()?;
let alert_flags = self.tape_alert_flags()
.map(|flags| format!("{:?}", flags))
.ok();
let mut status = LtoDriveAndMediaStatus {
blocksize: 0, // fixme: remove
density: None, // fixme
status: String::from("FIXME"),
options,
blocksize: drive_status.block_length,
compression: drive_status.compression,
buffer_mode: drive_status.buffer_mode,
density: TapeDensity::try_from(drive_status.density_code)?,
alert_flags,
file_number,
block_number,
write_protect: None,
file_number: None,
block_number: None,
manufactured: None,
bytes_read: None,
bytes_written: None,
@ -147,7 +146,16 @@ impl LtoTapeHandle {
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() {

View File

@ -10,7 +10,7 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag};
use proxmox::{
sys::error::SysResult,
tools::io::ReadExt,
tools::io::{ReadExt, WriteExt},
};
use crate::{
@ -39,7 +39,11 @@ use crate::{
SenseInfo,
ScsiError,
InquiryInfo,
ModeParameterHeader,
ModeBlockDescriptor,
alloc_page_aligned_buffer,
scsi_inquiry,
scsi_mode_sense,
},
};
@ -54,6 +58,42 @@ pub struct ReadPositionLongPage {
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 {
file: File,
}
@ -378,19 +418,19 @@ impl SgTape {
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)?;
sg_raw.set_timeout(30); // use short timeout
let mut cmd = Vec::new();
cmd.extend(&[0x00, 0, 0, 0, 0, 0]); // TEST UNIT READY
// fixme: check sense
sg_raw.do_command(&cmd)
.map_err(|err| format_err!("unit not ready - {}", err))?;
Ok(true)
match sg_raw.do_command(&cmd) {
Ok(_) => Ok(()),
Err(err) => {
bail!("test_unit_ready failed - {}", err);
}
}
}
pub fn wait_until_ready(&mut self) -> Result<(), Error> {
@ -400,7 +440,7 @@ impl SgTape {
loop {
match self.test_unit_ready() {
Ok(true) => return Ok(()),
Ok(()) => return Ok(()),
_ => {
std::thread::sleep(std::time::Duration::new(1, 0));
if start.elapsed()? > max_wait {
@ -522,6 +562,101 @@ impl SgTape {
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> {