tape: correctly set/display drive option
This commit is contained in:
parent
b717871d2a
commit
0892a512bc
@ -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>,
|
||||||
|
@ -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"))
|
||||||
|
@ -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() {
|
||||||
|
|
||||||
|
@ -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(¶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> {
|
pub struct SgTapeReader<'a> {
|
||||||
|
Loading…
Reference in New Issue
Block a user