[pbs-devel] [PATCH 09/11] tape: correctly set/display drive option
Dietmar Maurer
dietmar at proxmox.com
Wed Apr 7 12:23:06 CEST 2021
---
src/api2/types/tape/drive.rs | 12 +--
src/bin/proxmox-tape.rs | 5 +-
src/tape/drive/lto/mod.rs | 48 ++++++-----
src/tape/drive/lto/sg_tape.rs | 153 ++++++++++++++++++++++++++++++++--
4 files changed, 182 insertions(+), 36 deletions(-)
diff --git a/src/api2/types/tape/drive.rs b/src/api2/types/tape/drive.rs
index 058e544f..2a0857fd 100644
--- a/src/api2/types/tape/drive.rs
+++ b/src/api2/types/tape/drive.rs
@@ -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>,
diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs
index f1de6236..1d23b71c 100644
--- a/src/bin/proxmox-tape.rs
+++ b/src/bin/proxmox-tape.rs
@@ -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"))
diff --git a/src/tape/drive/lto/mod.rs b/src/tape/drive/lto/mod.rs
index 7fcef8a0..c9f3bf93 100644
--- a/src/tape/drive/lto/mod.rs
+++ b/src/tape/drive/lto/mod.rs
@@ -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() {
diff --git a/src/tape/drive/lto/sg_tape.rs b/src/tape/drive/lto/sg_tape.rs
index 9af0eae3..f3d71edd 100644
--- a/src/tape/drive/lto/sg_tape.rs
+++ b/src/tape/drive/lto/sg_tape.rs
@@ -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(¶m_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> {
--
2.20.1
More information about the pbs-devel
mailing list