[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(&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> {
-- 
2.20.1





More information about the pbs-devel mailing list