[pbs-devel] [PATCH proxmox-backup v3] tape: fix LTO locate_file for HP drives

Dietmar Maurer dietmar at proxmox.com
Tue Jun 29 08:35:55 CEST 2021


Add test code to the first locate_file command, compute locate_offset.
Subsequent locate_file commands use that offset.

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
Signed-off-by: Dietmar Maurer <dietmar at proxmox.com>
---

This is based on Dominiks patches:
 [PATCH proxmox-backup v2 1/2] tape/drive: add 'set_locate_offset' to TapeDriver Trait
 [PATCH proxmox-backup v2 2/2] api2/tape/restore: use file offset to compensate for some tape drives

Canges in v3:

 - Do not mofify the TapeDriver Trait. Instzead, do everything inside sg_tage.rs
 - Add special case for locate_file(1)

 src/tape/drive/lto/sg_tape.rs | 69 +++++++++++++++++++++++++++++++----
 1 file changed, 61 insertions(+), 8 deletions(-)

diff --git a/src/tape/drive/lto/sg_tape.rs b/src/tape/drive/lto/sg_tape.rs
index 25c239a2..4508f6f3 100644
--- a/src/tape/drive/lto/sg_tape.rs
+++ b/src/tape/drive/lto/sg_tape.rs
@@ -3,6 +3,7 @@ use std::fs::{File, OpenOptions};
 use std::os::unix::fs::OpenOptionsExt;
 use std::os::unix::io::AsRawFd;
 use std::path::Path;
+use std::convert::TryFrom;
 
 use anyhow::{bail, format_err, Error};
 use endian_trait::Endian;
@@ -122,6 +123,7 @@ pub struct LtoTapeStatus {
 
 pub struct SgTape {
     file: File,
+    locate_offset: Option<i64>,
     info: InquiryInfo,
     encryption_key_loaded: bool,
 }
@@ -145,6 +147,7 @@ impl SgTape {
             file,
             info,
             encryption_key_loaded: false,
+            locate_offset: None,
         })
     }
 
@@ -300,26 +303,76 @@ impl SgTape {
             return self.rewind();
         }
 
-        let position = position -1;
+        const SPACE_ONE_FILEMARK: &[u8] = &[0x11, 0x01, 0, 0, 1, 0];
+
+        // Special case for position 1, because LOCATE 0 does not work
+        if position == 1 {
+            self.rewind()?;
+            let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
+            sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
+            sg_raw.do_command(SPACE_ONE_FILEMARK)
+                .map_err(|err| format_err!("locate file {} (space) failed - {}", position, err))?;
+            return Ok(());
+        }
 
         let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
         sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
-        let mut cmd = Vec::new();
+
         // Note: LOCATE(16) works for LTO4 or newer
+        //
+        // It seems the LOCATE command behaves slightly different across vendors
+        // e.g. for IBM drives, LOCATE 1 moves to File #2, but
+        // for HP drives, LOCATE 1 move to File #1
+
+        let fixed_position = if let Some(locate_offset) = self.locate_offset {
+            if locate_offset < 0 {
+                position.saturating_sub((-locate_offset) as u64)
+            } else {
+                position.saturating_add(locate_offset as u64)
+            }
+        } else {
+            position
+        };
+        // always sub(1), so that it works for IBM drives without locate_offset
+        let fixed_position = fixed_position.saturating_sub(1);
+
+        let mut cmd = Vec::new();
         cmd.extend(&[0x92, 0b000_01_000, 0, 0]); // LOCATE(16) filemarks
-        cmd.extend(&position.to_be_bytes());
+        cmd.extend(&fixed_position.to_be_bytes());
         cmd.extend(&[0, 0, 0, 0]);
 
         sg_raw.do_command(&cmd)
             .map_err(|err| format_err!("locate file {} failed - {}", position, err))?;
 
-        // move to other side of filemark
-        cmd.truncate(0);
-        cmd.extend(&[0x11, 0x01, 0, 0, 1, 0]); // SPACE(6) one filemarks
-
-        sg_raw.do_command(&cmd)
+        // LOCATE always position at the BOT side of the filemark, so
+        // we need to move to other side of filemark
+        sg_raw.do_command(SPACE_ONE_FILEMARK)
             .map_err(|err| format_err!("locate file {} (space) failed - {}", position, err))?;
 
+        if self.locate_offset.is_none() {
+            // check if we landed at correct position
+            let current_file = self.current_file_number()?;
+            if current_file != position {
+                let offset: i64 =
+                    i64::try_from((position as i128) - (current_file as i128)).map_err(|err| {
+                        format_err!(
+                            "locate_file: offset between {} and {} invalid: {}",
+                            position,
+                            current_file,
+                            err
+                        )
+                    })?;
+                self.locate_offset = Some(offset);
+                self.locate_file(position)?;
+                let current_file = self.current_file_number()?;
+                if current_file != position {
+                    bail!("locate_file: compensating offset did not work, aborting...");
+                }
+            } else {
+                self.locate_offset = Some(0);
+            }
+        }
+
         Ok(())
     }
 
-- 
2.30.2





More information about the pbs-devel mailing list