tape: rust fmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
@ -11,30 +11,28 @@
|
||||
//!
|
||||
//! - unability to detect EOT (you just get EIO)
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
|
||||
use proxmox_uuid::Uuid;
|
||||
|
||||
use pbs_api_types::{
|
||||
Fingerprint, MamAttribute, LtoDriveAndMediaStatus, LtoTapeDrive, Lp17VolumeStatistics,
|
||||
Fingerprint, Lp17VolumeStatistics, LtoDriveAndMediaStatus, LtoTapeDrive, MamAttribute,
|
||||
};
|
||||
use pbs_config::key_config::KeyConfig;
|
||||
use proxmox_sys::command::run_command;
|
||||
use pbs_tape::{
|
||||
TapeWrite, TapeRead, BlockReadError, MediaContentHeader,
|
||||
sg_tape::{SgTape, TapeAlertFlags},
|
||||
linux_list_drives::open_lto_tape_device,
|
||||
sg_tape::{SgTape, TapeAlertFlags},
|
||||
BlockReadError, MediaContentHeader, TapeRead, TapeWrite,
|
||||
};
|
||||
use proxmox_sys::command::run_command;
|
||||
|
||||
use crate::{
|
||||
tape::{
|
||||
drive::TapeDriver,
|
||||
file_formats::{PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, MediaSetLabel},
|
||||
},
|
||||
use crate::tape::{
|
||||
drive::TapeDriver,
|
||||
file_formats::{MediaSetLabel, PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0},
|
||||
};
|
||||
|
||||
/// Open a tape device
|
||||
@ -46,7 +44,6 @@ use crate::{
|
||||
/// - check block size
|
||||
/// - for autoloader only, try to reload ejected tapes
|
||||
pub fn open_lto_tape_drive(config: &LtoTapeDrive) -> Result<LtoTapeHandle, Error> {
|
||||
|
||||
proxmox_lang::try_block!({
|
||||
let file = open_lto_tape_device(&config.path)?;
|
||||
|
||||
@ -64,7 +61,15 @@ pub fn open_lto_tape_drive(config: &LtoTapeDrive) -> Result<LtoTapeHandle, Error
|
||||
handle.set_default_options()?;
|
||||
|
||||
Ok(handle)
|
||||
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", config.name, config.path, err))
|
||||
})
|
||||
.map_err(|err: Error| {
|
||||
format_err!(
|
||||
"open drive '{}' ({}) failed - {}",
|
||||
config.name,
|
||||
config.path,
|
||||
err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Lto Tape device handle
|
||||
@ -73,7 +78,6 @@ pub struct LtoTapeHandle {
|
||||
}
|
||||
|
||||
impl LtoTapeHandle {
|
||||
|
||||
/// Creates a new instance
|
||||
pub fn new(file: File) -> Result<Self, Error> {
|
||||
let sg_tape = SgTape::new(file)?;
|
||||
@ -93,7 +97,8 @@ impl LtoTapeHandle {
|
||||
block_length: Option<u32>,
|
||||
buffer_mode: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
self.sg_tape.set_drive_options(compression, block_length, buffer_mode)
|
||||
self.sg_tape
|
||||
.set_drive_options(compression, block_length, buffer_mode)
|
||||
}
|
||||
|
||||
/// Write a single EOF mark without flushing buffers
|
||||
@ -102,7 +107,7 @@ impl LtoTapeHandle {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
self.sg_tape.get_drive_and_media_status()
|
||||
}
|
||||
|
||||
@ -123,7 +128,7 @@ impl LtoTapeHandle {
|
||||
}
|
||||
|
||||
/// Position the tape after filemark count. Count 0 means BOT.
|
||||
pub fn locate_file(&mut self, position: u64) -> Result<(), Error> {
|
||||
pub fn locate_file(&mut self, position: u64) -> Result<(), Error> {
|
||||
self.sg_tape.locate_file(position)
|
||||
}
|
||||
|
||||
@ -131,14 +136,14 @@ impl LtoTapeHandle {
|
||||
self.sg_tape.erase_media(fast)
|
||||
}
|
||||
|
||||
pub fn load(&mut self) -> Result<(), Error> {
|
||||
pub fn load(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape.load()
|
||||
}
|
||||
|
||||
/// Read Cartridge Memory (MAM Attributes)
|
||||
pub fn cartridge_memory(&mut self) -> Result<Vec<MamAttribute>, Error> {
|
||||
self.sg_tape.cartridge_memory()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read Volume Statistics
|
||||
pub fn volume_statistics(&mut self) -> Result<Lp17VolumeStatistics, Error> {
|
||||
@ -146,21 +151,21 @@ impl LtoTapeHandle {
|
||||
}
|
||||
|
||||
/// Lock the drive door
|
||||
pub fn lock(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape.set_medium_removal(false)
|
||||
pub fn lock(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape
|
||||
.set_medium_removal(false)
|
||||
.map_err(|err| format_err!("lock door failed - {}", err))
|
||||
}
|
||||
|
||||
/// Unlock the drive door
|
||||
pub fn unlock(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape.set_medium_removal(true)
|
||||
pub fn unlock(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape
|
||||
.set_medium_removal(true)
|
||||
.map_err(|err| format_err!("unlock door failed - {}", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TapeDriver for LtoTapeHandle {
|
||||
|
||||
fn sync(&mut self) -> Result<(), Error> {
|
||||
self.sg_tape.sync()?;
|
||||
Ok(())
|
||||
@ -172,7 +177,6 @@ impl TapeDriver for LtoTapeHandle {
|
||||
}
|
||||
|
||||
fn move_to_last_file(&mut self) -> Result<(), Error> {
|
||||
|
||||
self.move_to_eom(false)?;
|
||||
|
||||
self.sg_tape.check_filemark()?;
|
||||
@ -226,7 +230,6 @@ impl TapeDriver for LtoTapeHandle {
|
||||
media_set_label: &MediaSetLabel,
|
||||
key_config: Option<&KeyConfig>,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
let file_number = self.current_file_number()?;
|
||||
if file_number != 1 {
|
||||
self.rewind()?;
|
||||
@ -235,12 +238,16 @@ impl TapeDriver for LtoTapeHandle {
|
||||
|
||||
let file_number = self.current_file_number()?;
|
||||
if file_number != 1 {
|
||||
bail!("write_media_set_label failed - got wrong file number ({} != 1)", file_number);
|
||||
bail!(
|
||||
"write_media_set_label failed - got wrong file number ({} != 1)",
|
||||
file_number
|
||||
);
|
||||
}
|
||||
|
||||
self.set_encryption(None)?;
|
||||
|
||||
{ // limit handle scope
|
||||
{
|
||||
// limit handle scope
|
||||
let mut handle = self.write_file()?;
|
||||
|
||||
let mut value = serde_json::to_value(media_set_label)?;
|
||||
@ -257,7 +264,8 @@ impl TapeDriver for LtoTapeHandle {
|
||||
|
||||
let raw = serde_json::to_string_pretty(&value)?;
|
||||
|
||||
let header = MediaContentHeader::new(PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, raw.len() as u32);
|
||||
let header =
|
||||
MediaContentHeader::new(PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, raw.len() as u32);
|
||||
handle.write_header(&header, raw.as_bytes())?;
|
||||
handle.finish(false)?;
|
||||
}
|
||||
@ -285,15 +293,11 @@ impl TapeDriver for LtoTapeHandle {
|
||||
&mut self,
|
||||
key_fingerprint: Option<(Fingerprint, Uuid)>,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
if nix::unistd::Uid::effective().is_root() {
|
||||
|
||||
if let Some((ref key_fingerprint, ref uuid)) = key_fingerprint {
|
||||
|
||||
let (key_map, _digest) = pbs_config::tape_encryption_keys::load_keys()?;
|
||||
match key_map.get(key_fingerprint) {
|
||||
Some(item) => {
|
||||
|
||||
// derive specialized key for each media-set
|
||||
|
||||
let mut tape_key = [0u8; 32];
|
||||
@ -305,7 +309,8 @@ impl TapeDriver for LtoTapeHandle {
|
||||
&uuid_bytes,
|
||||
10,
|
||||
openssl::hash::MessageDigest::sha256(),
|
||||
&mut tape_key)?;
|
||||
&mut tape_key,
|
||||
)?;
|
||||
|
||||
return self.sg_tape.set_encryption(Some(tape_key));
|
||||
}
|
||||
@ -318,10 +323,11 @@ impl TapeDriver for LtoTapeHandle {
|
||||
|
||||
let output = if let Some((fingerprint, uuid)) = key_fingerprint {
|
||||
let fingerprint = fingerprint.signature();
|
||||
run_sg_tape_cmd("encryption", &[
|
||||
"--fingerprint", &fingerprint,
|
||||
"--uuid", &uuid.to_string(),
|
||||
], self.sg_tape.file_mut().as_raw_fd())?
|
||||
run_sg_tape_cmd(
|
||||
"encryption",
|
||||
&["--fingerprint", &fingerprint, "--uuid", &uuid.to_string()],
|
||||
self.sg_tape.file_mut().as_raw_fd(),
|
||||
)?
|
||||
} else {
|
||||
run_sg_tape_cmd("encryption", &[], self.sg_tape.file_mut().as_raw_fd())?
|
||||
};
|
||||
@ -331,12 +337,12 @@ impl TapeDriver for LtoTapeHandle {
|
||||
}
|
||||
|
||||
fn run_sg_tape_cmd(subcmd: &str, args: &[&str], fd: RawFd) -> Result<String, Error> {
|
||||
let mut command = std::process::Command::new(
|
||||
"/usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd");
|
||||
let mut command =
|
||||
std::process::Command::new("/usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd");
|
||||
command.args(&[subcmd]);
|
||||
command.args(&["--stdin"]);
|
||||
command.args(args);
|
||||
let device_fd = nix::unistd::dup(fd)?;
|
||||
command.stdin(unsafe { std::process::Stdio::from_raw_fd(device_fd)});
|
||||
command.stdin(unsafe { std::process::Stdio::from_raw_fd(device_fd) });
|
||||
run_command(command, None)
|
||||
}
|
||||
|
@ -8,55 +8,40 @@ pub use lto::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::sys::stat::Mode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use proxmox_sys::fs::{
|
||||
lock_file,
|
||||
atomic_open_or_create_file,
|
||||
file_read_optional_string,
|
||||
replace_file,
|
||||
CreateOptions,
|
||||
atomic_open_or_create_file, file_read_optional_string, lock_file, replace_file, CreateOptions,
|
||||
};
|
||||
|
||||
use proxmox_io::ReadExt;
|
||||
use proxmox_section_config::SectionConfigData;
|
||||
use proxmox_uuid::Uuid;
|
||||
use proxmox_sys::{task_log, WorkerTaskContext};
|
||||
use proxmox_uuid::Uuid;
|
||||
|
||||
use pbs_api_types::{VirtualTapeDrive, LtoTapeDrive, Fingerprint};
|
||||
use pbs_api_types::{Fingerprint, LtoTapeDrive, VirtualTapeDrive};
|
||||
use pbs_config::key_config::KeyConfig;
|
||||
|
||||
use pbs_tape::{
|
||||
TapeWrite, TapeRead, BlockReadError, MediaContentHeader,
|
||||
sg_tape::TapeAlertFlags,
|
||||
};
|
||||
use pbs_tape::{sg_tape::TapeAlertFlags, BlockReadError, MediaContentHeader, TapeRead, TapeWrite};
|
||||
|
||||
use crate::{
|
||||
server::send_load_media_email,
|
||||
tape::{
|
||||
MediaId,
|
||||
drive::{
|
||||
virtual_tape::open_virtual_tape_drive,
|
||||
},
|
||||
changer::{MediaChange, MtxMediaChanger},
|
||||
drive::virtual_tape::open_virtual_tape_drive,
|
||||
file_formats::{
|
||||
PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0,
|
||||
MediaLabel, MediaSetLabel, PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0,
|
||||
PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0,
|
||||
MediaLabel,
|
||||
MediaSetLabel,
|
||||
},
|
||||
changer::{
|
||||
MediaChange,
|
||||
MtxMediaChanger,
|
||||
},
|
||||
MediaId,
|
||||
},
|
||||
};
|
||||
|
||||
/// Tape driver interface
|
||||
pub trait TapeDriver {
|
||||
|
||||
/// Flush all data to the tape
|
||||
fn sync(&mut self) -> Result<(), Error>;
|
||||
|
||||
@ -90,14 +75,14 @@ pub trait TapeDriver {
|
||||
|
||||
/// Write label to tape (erase tape content)
|
||||
fn label_tape(&mut self, label: &MediaLabel) -> Result<(), Error> {
|
||||
|
||||
self.set_encryption(None)?;
|
||||
|
||||
self.format_media(true)?; // this rewinds the tape
|
||||
|
||||
let raw = serde_json::to_string_pretty(&serde_json::to_value(&label)?)?;
|
||||
|
||||
let header = MediaContentHeader::new(PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0, raw.len() as u32);
|
||||
let header =
|
||||
MediaContentHeader::new(PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0, raw.len() as u32);
|
||||
|
||||
{
|
||||
let mut writer = self.write_file()?;
|
||||
@ -125,7 +110,6 @@ pub trait TapeDriver {
|
||||
/// This tries to read both media labels (label and
|
||||
/// media_set_label). Also returns the optional encryption key configuration.
|
||||
fn read_label(&mut self) -> Result<(Option<MediaId>, Option<KeyConfig>), Error> {
|
||||
|
||||
self.rewind()?;
|
||||
|
||||
let label = {
|
||||
@ -143,7 +127,7 @@ pub trait TapeDriver {
|
||||
};
|
||||
|
||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||
header.check(PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0, 1, 64*1024)?;
|
||||
header.check(PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0, 1, 64 * 1024)?;
|
||||
let data = reader.read_exact_allocated(header.size as usize)?;
|
||||
|
||||
let label: MediaLabel = serde_json::from_slice(&data)
|
||||
@ -157,7 +141,10 @@ pub trait TapeDriver {
|
||||
label
|
||||
};
|
||||
|
||||
let mut media_id = MediaId { label, media_set_label: None };
|
||||
let mut media_id = MediaId {
|
||||
label,
|
||||
media_set_label: None,
|
||||
};
|
||||
|
||||
// try to read MediaSet label
|
||||
let mut reader = match self.read_next_file() {
|
||||
@ -174,7 +161,7 @@ pub trait TapeDriver {
|
||||
};
|
||||
|
||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||
header.check(PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, 1, 64*1024)?;
|
||||
header.check(PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, 1, 64 * 1024)?;
|
||||
let data = reader.read_exact_allocated(header.size as usize)?;
|
||||
|
||||
let mut data: Value = serde_json::from_slice(&data)
|
||||
@ -238,28 +225,25 @@ pub fn media_changer(
|
||||
config: &SectionConfigData,
|
||||
drive: &str,
|
||||
) -> Result<Option<(Box<dyn MediaChange>, String)>, Error> {
|
||||
|
||||
match config.sections.get(drive) {
|
||||
Some((section_type_name, config)) => {
|
||||
match section_type_name.as_ref() {
|
||||
"virtual" => {
|
||||
let tape = VirtualTapeDrive::deserialize(config)?;
|
||||
Ok(Some((Box::new(tape), drive.to_string())))
|
||||
}
|
||||
"lto" => {
|
||||
let drive_config = LtoTapeDrive::deserialize(config)?;
|
||||
match drive_config.changer {
|
||||
Some(ref changer_name) => {
|
||||
let changer = MtxMediaChanger::with_drive_config(&drive_config)?;
|
||||
let changer_name = changer_name.to_string();
|
||||
Ok(Some((Box::new(changer), changer_name)))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
ty => bail!("unknown drive type '{}' - internal error", ty),
|
||||
Some((section_type_name, config)) => match section_type_name.as_ref() {
|
||||
"virtual" => {
|
||||
let tape = VirtualTapeDrive::deserialize(config)?;
|
||||
Ok(Some((Box::new(tape), drive.to_string())))
|
||||
}
|
||||
}
|
||||
"lto" => {
|
||||
let drive_config = LtoTapeDrive::deserialize(config)?;
|
||||
match drive_config.changer {
|
||||
Some(ref changer_name) => {
|
||||
let changer = MtxMediaChanger::with_drive_config(&drive_config)?;
|
||||
let changer_name = changer_name.to_string();
|
||||
Ok(Some((Box::new(changer), changer_name)))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
ty => bail!("unknown drive type '{}' - internal error", ty),
|
||||
},
|
||||
None => {
|
||||
bail!("no such drive '{}'", drive);
|
||||
}
|
||||
@ -274,40 +258,30 @@ pub fn required_media_changer(
|
||||
drive: &str,
|
||||
) -> Result<(Box<dyn MediaChange>, String), Error> {
|
||||
match media_changer(config, drive) {
|
||||
Ok(Some(result)) => {
|
||||
Ok(result)
|
||||
}
|
||||
Ok(Some(result)) => Ok(result),
|
||||
Ok(None) => {
|
||||
bail!("drive '{}' has no associated changer device", drive);
|
||||
},
|
||||
Err(err) => {
|
||||
Err(err)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens a tape drive (this fails if there is no media loaded)
|
||||
pub fn open_drive(
|
||||
config: &SectionConfigData,
|
||||
drive: &str,
|
||||
) -> Result<Box<dyn TapeDriver>, Error> {
|
||||
|
||||
pub fn open_drive(config: &SectionConfigData, drive: &str) -> Result<Box<dyn TapeDriver>, Error> {
|
||||
match config.sections.get(drive) {
|
||||
Some((section_type_name, config)) => {
|
||||
match section_type_name.as_ref() {
|
||||
"virtual" => {
|
||||
let tape = VirtualTapeDrive::deserialize(config)?;
|
||||
let handle = open_virtual_tape_drive(&tape)?;
|
||||
Ok(Box::new(handle))
|
||||
}
|
||||
"lto" => {
|
||||
let tape = LtoTapeDrive::deserialize(config)?;
|
||||
let handle = open_lto_tape_drive(&tape)?;
|
||||
Ok(Box::new(handle))
|
||||
}
|
||||
ty => bail!("unknown drive type '{}' - internal error", ty),
|
||||
Some((section_type_name, config)) => match section_type_name.as_ref() {
|
||||
"virtual" => {
|
||||
let tape = VirtualTapeDrive::deserialize(config)?;
|
||||
let handle = open_virtual_tape_drive(&tape)?;
|
||||
Ok(Box::new(handle))
|
||||
}
|
||||
}
|
||||
"lto" => {
|
||||
let tape = LtoTapeDrive::deserialize(config)?;
|
||||
let handle = open_lto_tape_drive(&tape)?;
|
||||
Ok(Box::new(handle))
|
||||
}
|
||||
ty => bail!("unknown drive type '{}' - internal error", ty),
|
||||
},
|
||||
None => {
|
||||
bail!("no such drive '{}'", drive);
|
||||
}
|
||||
@ -328,7 +302,7 @@ impl std::fmt::Display for TapeRequestError {
|
||||
match self {
|
||||
TapeRequestError::None => {
|
||||
write!(f, "no error")
|
||||
},
|
||||
}
|
||||
TapeRequestError::OpenFailed(reason) => {
|
||||
write!(f, "tape open failed - {}", reason)
|
||||
}
|
||||
@ -336,7 +310,10 @@ impl std::fmt::Display for TapeRequestError {
|
||||
write!(f, "wrong media label {}", label)
|
||||
}
|
||||
TapeRequestError::EmptyTape => {
|
||||
write!(f, "found empty media without label (please label all tapes first)")
|
||||
write!(
|
||||
f,
|
||||
"found empty media without label (please label all tapes first)"
|
||||
)
|
||||
}
|
||||
TapeRequestError::ReadFailed(reason) => {
|
||||
write!(f, "tape read failed - {}", reason)
|
||||
@ -356,11 +333,7 @@ pub fn request_and_load_media(
|
||||
drive: &str,
|
||||
label: &MediaLabel,
|
||||
notify_email: &Option<String>,
|
||||
) -> Result<(
|
||||
Box<dyn TapeDriver>,
|
||||
MediaId,
|
||||
), Error> {
|
||||
|
||||
) -> Result<(Box<dyn TapeDriver>, MediaId), Error> {
|
||||
let check_label = |handle: &mut dyn TapeDriver, uuid: &proxmox_uuid::Uuid| {
|
||||
if let Ok((Some(media_id), _)) = handle.read_label() {
|
||||
task_log!(
|
||||
@ -399,13 +372,18 @@ pub fn request_and_load_media(
|
||||
let label_text = label.label_text.clone();
|
||||
|
||||
if drive_config.changer.is_some() {
|
||||
|
||||
task_log!(worker, "loading media '{}' into drive '{}'", label_text, drive);
|
||||
task_log!(
|
||||
worker,
|
||||
"loading media '{}' into drive '{}'",
|
||||
label_text,
|
||||
drive
|
||||
);
|
||||
|
||||
let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
|
||||
changer.load_media(&label_text)?;
|
||||
|
||||
let mut handle: Box<dyn TapeDriver> = Box::new(open_lto_tape_drive(&drive_config)?);
|
||||
let mut handle: Box<dyn TapeDriver> =
|
||||
Box::new(open_lto_tape_drive(&drive_config)?);
|
||||
|
||||
let media_id = check_label(handle.as_mut(), &label.uuid)?;
|
||||
|
||||
@ -415,34 +393,34 @@ pub fn request_and_load_media(
|
||||
let mut last_error = TapeRequestError::None;
|
||||
|
||||
let update_and_log_request_error =
|
||||
|old: &mut TapeRequestError, new: TapeRequestError| -> Result<(), Error>
|
||||
{
|
||||
if new != *old {
|
||||
task_log!(worker, "{}", new);
|
||||
task_log!(
|
||||
worker,
|
||||
"Please insert media '{}' into drive '{}'",
|
||||
label_text,
|
||||
drive
|
||||
);
|
||||
if let Some(to) = notify_email {
|
||||
send_load_media_email(
|
||||
drive,
|
||||
&label_text,
|
||||
to,
|
||||
Some(new.to_string()),
|
||||
)?;
|
||||
|old: &mut TapeRequestError, new: TapeRequestError| -> Result<(), Error> {
|
||||
if new != *old {
|
||||
task_log!(worker, "{}", new);
|
||||
task_log!(
|
||||
worker,
|
||||
"Please insert media '{}' into drive '{}'",
|
||||
label_text,
|
||||
drive
|
||||
);
|
||||
if let Some(to) = notify_email {
|
||||
send_load_media_email(
|
||||
drive,
|
||||
&label_text,
|
||||
to,
|
||||
Some(new.to_string()),
|
||||
)?;
|
||||
}
|
||||
*old = new;
|
||||
}
|
||||
*old = new;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
Ok(())
|
||||
};
|
||||
|
||||
loop {
|
||||
worker.check_abort()?;
|
||||
|
||||
if last_error != TapeRequestError::None {
|
||||
for _ in 0..50 { // delay 5 seconds
|
||||
for _ in 0..50 {
|
||||
// delay 5 seconds
|
||||
worker.check_abort()?;
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
@ -484,12 +462,8 @@ pub fn request_and_load_media(
|
||||
);
|
||||
TapeRequestError::WrongLabel(label_string)
|
||||
}
|
||||
Ok((None, _)) => {
|
||||
TapeRequestError::EmptyTape
|
||||
}
|
||||
Err(err) => {
|
||||
TapeRequestError::ReadFailed(err.to_string())
|
||||
}
|
||||
Ok((None, _)) => TapeRequestError::EmptyTape,
|
||||
Err(err) => TapeRequestError::ReadFailed(err.to_string()),
|
||||
};
|
||||
|
||||
update_and_log_request_error(&mut last_error, request_error)?;
|
||||
@ -537,11 +511,7 @@ pub fn lock_tape_device(
|
||||
/// Writes the given state for the specified drive
|
||||
///
|
||||
/// This function does not lock, so make sure the drive is locked
|
||||
pub fn set_tape_device_state(
|
||||
drive: &str,
|
||||
state: &str,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
pub fn set_tape_device_state(drive: &str, state: &str) -> Result<(), Error> {
|
||||
let mut path = PathBuf::from(crate::tape::DRIVE_STATE_DIR);
|
||||
path.push(drive);
|
||||
|
||||
@ -571,19 +541,12 @@ pub fn get_tape_device_state(
|
||||
}
|
||||
}
|
||||
|
||||
fn tape_device_path(
|
||||
config: &SectionConfigData,
|
||||
drive: &str,
|
||||
) -> Result<String, Error> {
|
||||
fn tape_device_path(config: &SectionConfigData, drive: &str) -> Result<String, Error> {
|
||||
match config.sections.get(drive) {
|
||||
Some((section_type_name, config)) => {
|
||||
let path = match section_type_name.as_ref() {
|
||||
"virtual" => {
|
||||
VirtualTapeDrive::deserialize(config)?.path
|
||||
}
|
||||
"lto" => {
|
||||
LtoTapeDrive::deserialize(config)?.path
|
||||
}
|
||||
"virtual" => VirtualTapeDrive::deserialize(config)?.path,
|
||||
"lto" => LtoTapeDrive::deserialize(config)?.path,
|
||||
ty => bail!("unknown drive type '{}' - internal error", ty),
|
||||
};
|
||||
Ok(path)
|
||||
@ -622,7 +585,7 @@ fn open_device_lock(device_path: &str) -> Result<std::fs::File, Error> {
|
||||
// Acquires an exclusive lock on `device_path`
|
||||
//
|
||||
fn lock_device_path(device_path: &str) -> Result<DeviceLockGuard, TapeLockError> {
|
||||
let mut file = open_device_lock(device_path)?;
|
||||
let mut file = open_device_lock(device_path)?;
|
||||
let timeout = std::time::Duration::new(10, 0);
|
||||
if let Err(err) = lock_file(&mut file, true, Some(timeout)) {
|
||||
if err.kind() == std::io::ErrorKind::Interrupted {
|
||||
@ -638,13 +601,12 @@ fn lock_device_path(device_path: &str) -> Result<DeviceLockGuard, TapeLockError>
|
||||
// Same logic as lock_device_path, but uses a timeout of 0, making it
|
||||
// non-blocking, and returning if the file is locked or not
|
||||
fn test_device_path_lock(device_path: &str) -> Result<bool, Error> {
|
||||
|
||||
let mut file = open_device_lock(device_path)?;
|
||||
let mut file = open_device_lock(device_path)?;
|
||||
|
||||
let timeout = std::time::Duration::new(0, 0);
|
||||
match lock_file(&mut file, true, Some(timeout)) {
|
||||
// file was not locked, continue
|
||||
Ok(()) => {},
|
||||
Ok(()) => {}
|
||||
// file was locked, return true
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => return Ok(true),
|
||||
Err(err) => bail!("{}", err),
|
||||
|
@ -4,40 +4,19 @@ use std::fs::File;
|
||||
use std::io;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use proxmox_sys::{
|
||||
fs::{replace_file, CreateOptions},
|
||||
};
|
||||
use proxmox_sys::fs::{replace_file, CreateOptions};
|
||||
|
||||
use pbs_config::key_config::KeyConfig;
|
||||
use pbs_tape::{
|
||||
TapeWrite,
|
||||
TapeRead,
|
||||
BlockedReader,
|
||||
BlockedWriter,
|
||||
BlockReadError,
|
||||
MtxStatus,
|
||||
DriveStatus,
|
||||
ElementStatus,
|
||||
StorageElementStatus,
|
||||
MediaContentHeader,
|
||||
EmulateTapeReader,
|
||||
EmulateTapeWriter,
|
||||
BlockReadError, BlockedReader, BlockedWriter, DriveStatus, ElementStatus, EmulateTapeReader,
|
||||
EmulateTapeWriter, MediaContentHeader, MtxStatus, StorageElementStatus, TapeRead, TapeWrite,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
tape::{
|
||||
drive::{
|
||||
VirtualTapeDrive,
|
||||
TapeDriver,
|
||||
MediaChange,
|
||||
},
|
||||
file_formats::{
|
||||
MediaSetLabel,
|
||||
PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0,
|
||||
},
|
||||
},
|
||||
use crate::tape::{
|
||||
drive::{MediaChange, TapeDriver, VirtualTapeDrive},
|
||||
file_formats::{MediaSetLabel, PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0},
|
||||
};
|
||||
|
||||
/// This needs to lock the drive
|
||||
@ -53,24 +32,32 @@ pub fn open_virtual_tape_drive(config: &VirtualTapeDrive) -> Result<VirtualTapeH
|
||||
Ok(VirtualTapeHandle {
|
||||
_lock: lock,
|
||||
drive_name: config.name.clone(),
|
||||
max_size: config.max_size.unwrap_or(64*1024*1024),
|
||||
max_size: config.max_size.unwrap_or(64 * 1024 * 1024),
|
||||
path: std::path::PathBuf::from(&config.path),
|
||||
})
|
||||
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", config.name, config.path, err))
|
||||
})
|
||||
.map_err(|err: Error| {
|
||||
format_err!(
|
||||
"open drive '{}' ({}) failed - {}",
|
||||
config.name,
|
||||
config.path,
|
||||
err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct VirtualTapeStatus {
|
||||
name: String,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct VirtualDriveStatus {
|
||||
current_tape: Option<VirtualTapeStatus>,
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TapeIndex {
|
||||
files: usize,
|
||||
}
|
||||
@ -83,7 +70,6 @@ pub struct VirtualTapeHandle {
|
||||
}
|
||||
|
||||
impl VirtualTapeHandle {
|
||||
|
||||
fn status_file_path(&self) -> std::path::PathBuf {
|
||||
let mut path = self.path.clone();
|
||||
path.push("drive-status.json");
|
||||
@ -121,11 +107,11 @@ impl VirtualTapeHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn truncate_tape(&self, tape_name: &str, pos: usize) -> Result<usize, Error> {
|
||||
fn truncate_tape(&self, tape_name: &str, pos: usize) -> Result<usize, Error> {
|
||||
let mut index = self.load_tape_index(tape_name)?;
|
||||
|
||||
if index.files <= pos {
|
||||
return Ok(index.files)
|
||||
return Ok(index.files);
|
||||
}
|
||||
|
||||
for i in pos..index.files {
|
||||
@ -143,9 +129,7 @@ impl VirtualTapeHandle {
|
||||
fn load_status(&self) -> Result<VirtualDriveStatus, Error> {
|
||||
let path = self.status_file_path();
|
||||
|
||||
let default = serde_json::to_value(VirtualDriveStatus {
|
||||
current_tape: None,
|
||||
})?;
|
||||
let default = serde_json::to_value(VirtualDriveStatus { current_tape: None })?;
|
||||
|
||||
let data = proxmox_sys::fs::file_get_json(&path, Some(default))?;
|
||||
let status: VirtualDriveStatus = serde_json::from_value(data)?;
|
||||
@ -183,9 +167,12 @@ impl VirtualTapeHandle {
|
||||
fn forward_space_count_files(&mut self, count: usize) -> Result<(), Error> {
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
|
||||
let index = self.load_tape_index(name)
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
let index = self
|
||||
.load_tape_index(name)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
||||
|
||||
let new_pos = *pos + count;
|
||||
@ -210,7 +197,6 @@ impl VirtualTapeHandle {
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref mut pos, .. }) => {
|
||||
|
||||
if count <= *pos {
|
||||
*pos = *pos - count;
|
||||
} else {
|
||||
@ -225,28 +211,26 @@ impl VirtualTapeHandle {
|
||||
None => bail!("drive is empty (no tape loaded)."),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl TapeDriver for VirtualTapeHandle {
|
||||
|
||||
fn sync(&mut self) -> Result<(), Error> {
|
||||
Ok(()) // do nothing for now
|
||||
}
|
||||
|
||||
fn current_file_number(&mut self) -> Result<u64, Error> {
|
||||
let status = self.load_status()
|
||||
let status = self
|
||||
.load_status()
|
||||
.map_err(|err| format_err!("current_file_number failed: {}", err.to_string()))?;
|
||||
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { pos, .. }) => { Ok(pos as u64)},
|
||||
Some(VirtualTapeStatus { pos, .. }) => Ok(pos as u64),
|
||||
None => bail!("current_file_number failed: drive is empty (no tape loaded)."),
|
||||
}
|
||||
}
|
||||
|
||||
/// Move to last file
|
||||
fn move_to_last_file(&mut self) -> Result<(), Error> {
|
||||
|
||||
self.move_to_eom(false)?;
|
||||
|
||||
if self.current_file_number()? == 0 {
|
||||
@ -261,9 +245,12 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
fn move_to_file(&mut self, file: u64) -> Result<(), Error> {
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
|
||||
let index = self.load_tape_index(name)
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
let index = self
|
||||
.load_tape_index(name)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
||||
|
||||
if file as usize > index.files {
|
||||
@ -282,46 +269,55 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
}
|
||||
|
||||
fn read_next_file(&mut self) -> Result<Box<dyn TapeRead>, BlockReadError> {
|
||||
let mut status = self.load_status()
|
||||
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||
let mut status = self.load_status().map_err(|err| {
|
||||
BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string()))
|
||||
})?;
|
||||
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
|
||||
let index = self.load_tape_index(name)
|
||||
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
let index = self.load_tape_index(name).map_err(|err| {
|
||||
BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string()))
|
||||
})?;
|
||||
|
||||
if *pos >= index.files {
|
||||
return Err(BlockReadError::EndOfStream);
|
||||
}
|
||||
|
||||
let path = self.tape_file_path(name, *pos);
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(path)?;
|
||||
let file = std::fs::OpenOptions::new().read(true).open(path)?;
|
||||
|
||||
*pos += 1;
|
||||
self.store_status(&status)
|
||||
.map_err(|err| BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string())))?;
|
||||
self.store_status(&status).map_err(|err| {
|
||||
BlockReadError::Error(io::Error::new(io::ErrorKind::Other, err.to_string()))
|
||||
})?;
|
||||
|
||||
let reader = EmulateTapeReader::new(file);
|
||||
let reader = BlockedReader::open(reader)?;
|
||||
Ok(Box::new(reader))
|
||||
}
|
||||
None => {
|
||||
return Err(BlockReadError::Error(proxmox_lang::io_format_err!("drive is empty (no tape loaded).")));
|
||||
return Err(BlockReadError::Error(proxmox_lang::io_format_err!(
|
||||
"drive is empty (no tape loaded)."
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_file(&mut self) -> Result<Box<dyn TapeWrite>, io::Error> {
|
||||
let mut status = self.load_status()
|
||||
let mut status = self
|
||||
.load_status()
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
||||
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
|
||||
let mut index = self.load_tape_index(name)
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
let mut index = self
|
||||
.load_tape_index(name)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
||||
|
||||
for i in *pos..index.files {
|
||||
@ -333,7 +329,6 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
for i in 0..*pos {
|
||||
let path = self.tape_file_path(name, i);
|
||||
used_space += path.metadata()?.len() as usize;
|
||||
|
||||
}
|
||||
index.files = *pos + 1;
|
||||
|
||||
@ -369,9 +364,12 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
fn move_to_eom(&mut self, _write_missing_eof: bool) -> Result<(), Error> {
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
|
||||
let index = self.load_tape_index(name)
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
let index = self
|
||||
.load_tape_index(name)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))?;
|
||||
|
||||
*pos = index.files;
|
||||
@ -400,7 +398,10 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
fn format_media(&mut self, _fast: bool) -> Result<(), Error> {
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
*pos = self.truncate_tape(name, 0)?;
|
||||
self.store_status(&status)?;
|
||||
Ok(())
|
||||
@ -414,7 +415,6 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
media_set_label: &MediaSetLabel,
|
||||
key_config: Option<&KeyConfig>,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
self.set_encryption(None)?;
|
||||
|
||||
if key_config.is_some() {
|
||||
@ -423,7 +423,10 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
|
||||
let mut status = self.load_status()?;
|
||||
match status.current_tape {
|
||||
Some(VirtualTapeStatus { ref name, ref mut pos }) => {
|
||||
Some(VirtualTapeStatus {
|
||||
ref name,
|
||||
ref mut pos,
|
||||
}) => {
|
||||
*pos = self.truncate_tape(name, 1)?;
|
||||
let pos = *pos;
|
||||
self.store_status(&status)?;
|
||||
@ -432,11 +435,17 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
bail!("media is empty (no label).");
|
||||
}
|
||||
if pos != 1 {
|
||||
bail!("write_media_set_label: truncate failed - got wrong pos '{}'", pos);
|
||||
bail!(
|
||||
"write_media_set_label: truncate failed - got wrong pos '{}'",
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
let raw = serde_json::to_string_pretty(&serde_json::to_value(media_set_label)?)?;
|
||||
let header = MediaContentHeader::new(PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0, raw.len() as u32);
|
||||
let header = MediaContentHeader::new(
|
||||
PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0,
|
||||
raw.len() as u32,
|
||||
);
|
||||
|
||||
{
|
||||
let mut writer = self.write_file()?;
|
||||
@ -451,15 +460,12 @@ impl TapeDriver for VirtualTapeHandle {
|
||||
}
|
||||
|
||||
fn eject_media(&mut self) -> Result<(), Error> {
|
||||
let status = VirtualDriveStatus {
|
||||
current_tape: None,
|
||||
};
|
||||
let status = VirtualDriveStatus { current_tape: None };
|
||||
self.store_status(&status)
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaChange for VirtualTapeHandle {
|
||||
|
||||
fn drive_number(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
@ -469,7 +475,6 @@ impl MediaChange for VirtualTapeHandle {
|
||||
}
|
||||
|
||||
fn status(&mut self) -> Result<MtxStatus, Error> {
|
||||
|
||||
let drive_status = self.load_status()?;
|
||||
|
||||
let mut drives = Vec::new();
|
||||
@ -482,7 +487,7 @@ impl MediaChange for VirtualTapeHandle {
|
||||
vendor: None,
|
||||
model: None,
|
||||
element_address: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// This implementation is lame, because we do not have fixed
|
||||
@ -490,7 +495,7 @@ impl MediaChange for VirtualTapeHandle {
|
||||
|
||||
let mut slots = Vec::new();
|
||||
let label_texts = self.online_media_label_texts()?;
|
||||
let max_slots = ((label_texts.len() + 7)/8) * 8;
|
||||
let max_slots = ((label_texts.len() + 7) / 8) * 8;
|
||||
|
||||
for i in 0..max_slots {
|
||||
let status = if let Some(label_text) = label_texts.get(i) {
|
||||
@ -505,7 +510,11 @@ impl MediaChange for VirtualTapeHandle {
|
||||
});
|
||||
}
|
||||
|
||||
Ok(MtxStatus { drives, slots, transports: Vec::new() })
|
||||
Ok(MtxStatus {
|
||||
drives,
|
||||
slots,
|
||||
transports: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn transfer_media(&mut self, _from: u64, _to: u64) -> Result<MtxStatus, Error> {
|
||||
@ -568,7 +577,6 @@ impl MediaChange for VirtualTapeHandle {
|
||||
}
|
||||
|
||||
impl MediaChange for VirtualTapeDrive {
|
||||
|
||||
fn drive_number(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
|
Reference in New Issue
Block a user