tape: fix LTO locate_file for HP drives
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@proxmox.com> Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
fda19dcc6f
commit
414be8b675
@ -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(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user