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
f94aa5ceb1
commit
53e80e8aa2
|
@ -3,6 +3,7 @@ use std::fs::{File, OpenOptions};
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use endian_trait::Endian;
|
use endian_trait::Endian;
|
||||||
|
@ -122,6 +123,7 @@ pub struct LtoTapeStatus {
|
||||||
|
|
||||||
pub struct SgTape {
|
pub struct SgTape {
|
||||||
file: File,
|
file: File,
|
||||||
|
locate_offset: Option<i64>,
|
||||||
info: InquiryInfo,
|
info: InquiryInfo,
|
||||||
encryption_key_loaded: bool,
|
encryption_key_loaded: bool,
|
||||||
}
|
}
|
||||||
|
@ -145,6 +147,7 @@ impl SgTape {
|
||||||
file,
|
file,
|
||||||
info,
|
info,
|
||||||
encryption_key_loaded: false,
|
encryption_key_loaded: false,
|
||||||
|
locate_offset: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,26 +303,76 @@ impl SgTape {
|
||||||
return self.rewind();
|
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)?;
|
let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
|
||||||
sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
|
sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
|
||||||
let mut cmd = Vec::new();
|
|
||||||
// Note: LOCATE(16) works for LTO4 or newer
|
// 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(&[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]);
|
cmd.extend(&[0, 0, 0, 0]);
|
||||||
|
|
||||||
sg_raw.do_command(&cmd)
|
sg_raw.do_command(&cmd)
|
||||||
.map_err(|err| format_err!("locate file {} failed - {}", position, err))?;
|
.map_err(|err| format_err!("locate file {} failed - {}", position, err))?;
|
||||||
|
|
||||||
// move to other side of filemark
|
// LOCATE always position at the BOT side of the filemark, so
|
||||||
cmd.truncate(0);
|
// we need to move to other side of filemark
|
||||||
cmd.extend(&[0x11, 0x01, 0, 0, 1, 0]); // SPACE(6) one filemarks
|
sg_raw.do_command(SPACE_ONE_FILEMARK)
|
||||||
|
|
||||||
sg_raw.do_command(&cmd)
|
|
||||||
.map_err(|err| format_err!("locate file {} (space) failed - {}", position, err))?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue