From d5a48b5ce47551e20ee7c63d4d97792e2b414986 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 18 Jan 2021 07:16:06 +0100 Subject: [PATCH] tape: add hardware encryption key managenent api --- src/api2/config.rs | 2 + src/api2/config/tape_encryption_keys.rs | 166 ++++++++++++++++++++++++ src/api2/tape/drive.rs | 8 +- src/api2/types/mod.rs | 23 ++-- src/api2/types/tape/drive.rs | 18 ++- src/backup/crypt_config.rs | 2 +- src/bin/proxmox-tape.rs | 1 + src/bin/proxmox_tape/encryption_key.rs | 70 ++++++++++ src/bin/proxmox_tape/mod.rs | 3 + src/bin/sg-tape-cmd.rs | 56 +++++++- src/config.rs | 1 + src/config/tape_encryption_keys.rs | 126 ++++++++++++++++++ src/tape/drive/linux_tape.rs | 40 ++++++ src/tape/drive/mod.rs | 17 ++- src/tape/drive/virtual_tape.rs | 22 ++-- 15 files changed, 518 insertions(+), 37 deletions(-) create mode 100644 src/api2/config/tape_encryption_keys.rs create mode 100644 src/bin/proxmox_tape/encryption_key.rs create mode 100644 src/config/tape_encryption_keys.rs diff --git a/src/api2/config.rs b/src/api2/config.rs index 7ad43f9f..fe550b99 100644 --- a/src/api2/config.rs +++ b/src/api2/config.rs @@ -9,6 +9,7 @@ pub mod verify; pub mod drive; pub mod changer; pub mod media_pool; +pub mod tape_encryption_keys; const SUBDIRS: SubdirMap = &[ ("access", &access::ROUTER), @@ -18,6 +19,7 @@ const SUBDIRS: SubdirMap = &[ ("media-pool", &media_pool::ROUTER), ("remote", &remote::ROUTER), ("sync", &sync::ROUTER), + ("tape-encryption-keys", &tape_encryption_keys::ROUTER), ("verify", &verify::ROUTER), ]; diff --git a/src/api2/config/tape_encryption_keys.rs b/src/api2/config/tape_encryption_keys.rs new file mode 100644 index 00000000..09d1443b --- /dev/null +++ b/src/api2/config/tape_encryption_keys.rs @@ -0,0 +1,166 @@ +use anyhow::{bail, Error}; +use serde_json::Value; + +use proxmox::{ + api::{ + api, + ApiMethod, + Router, + RpcEnvironment, + }, + tools::fs::open_file_locked, +}; + +use crate::{ + config::{ + tape_encryption_keys::{ + TAPE_KEYS_LOCKFILE, + EncryptionKeyInfo, + load_keys, + save_keys, + }, + }, + api2::types::{ + TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + PROXMOX_CONFIG_DIGEST_SCHEMA, + TapeKeyMetadata, + }, + backup::Fingerprint, + tools::format::as_fingerprint, +}; + +#[api( + protected: true, + input: { + properties: {}, + }, + returns: { + description: "The list of tape encryption keys (with config digest).", + type: Array, + items: { type: TapeKeyMetadata }, + }, +)] +/// List existing keys +pub fn list_keys( + _param: Value, + _info: &ApiMethod, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + + let (key_map, digest) = load_keys()?; + + let mut list = Vec::new(); + + for (_fingerprint, item) in key_map { + list.push(TapeKeyMetadata { + hint: item.hint, + fingerprint: as_fingerprint(item.fingerprint.bytes()), + }); + } + + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); + + Ok(list) +} +#[api( + protected: true, + input: { + properties: { + password: { + description: "A secret password.", + min_length: 5, + }, + hint: { + description: "Password restore hint", + min_length: 1, + }, + }, + }, +)] +/// Create a new encryption key +pub fn create_key( + password: String, + hint: String, + _rpcenv: &mut dyn RpcEnvironment +) -> Result { + + let key = openssl::sha::sha256(password.as_bytes()); // fixme: better KDF ?? + + let item = EncryptionKeyInfo::new(&key, hint); + + let _lock = open_file_locked( + TAPE_KEYS_LOCKFILE, + std::time::Duration::new(10, 0), + true, + )?; + + let (mut key_map, _) = load_keys()?; + + let fingerprint = item.fingerprint.clone(); + + if let Some(_) = key_map.get(&fingerprint) { + bail!("encryption key '{}' already exists.", fingerprint); + } + + key_map.insert(fingerprint.clone(), item); + save_keys(key_map)?; + + Ok(fingerprint) +} + + +#[api( + protected: true, + input: { + properties: { + fingerprint: { + schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + }, + digest: { + optional: true, + schema: PROXMOX_CONFIG_DIGEST_SCHEMA, + }, + }, + }, +)] +/// Remove a encryption key from the database +/// +/// Please note that you can no longer access tapes using this key. +pub fn delete_key( + fingerprint: Fingerprint, + digest: Option, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let _lock = open_file_locked( + TAPE_KEYS_LOCKFILE, + std::time::Duration::new(10, 0), + true, + )?; + + let (mut key_map, expected_digest) = load_keys()?; + + if let Some(ref digest) = digest { + let digest = proxmox::tools::hex_to_digest(digest)?; + crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + } + + match key_map.get(&fingerprint) { + Some(_) => { key_map.remove(&fingerprint); }, + None => bail!("tape encryption key '{}' does not exist.", fingerprint), + } + + save_keys(key_map)?; + + Ok(()) +} + +const ITEM_ROUTER: Router = Router::new() + //.get(&API_METHOD_READ_KEY_METADATA) + //.put(&API_METHOD_UPDATE_KEY_METADATA) + .delete(&API_METHOD_DELETE_KEY); + +pub const ROUTER: Router = Router::new() + .get(&API_METHOD_LIST_KEYS) + .post(&API_METHOD_CREATE_KEY) + .match_all("fingerprint", &ITEM_ROUTER); diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs index bac19847..7ab4f484 100644 --- a/src/api2/tape/drive.rs +++ b/src/api2/tape/drive.rs @@ -1,7 +1,7 @@ use std::path::Path; use std::sync::Arc; -use anyhow::{bail, format_err, Error}; +use anyhow::{bail, Error}; use serde_json::Value; use proxmox::{ @@ -880,8 +880,7 @@ pub fn cartridge_memory(drive: String) -> Result, Error> { let (config, _digest) = config::drive::config()?; let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?; - let mut handle = drive_config.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, drive_config.path, err))?; + let mut handle = drive_config.open()?; handle.cartridge_memory() } @@ -906,8 +905,7 @@ pub fn status(drive: String) -> Result { let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?; // Note: use open_linux_tape_device, because this also works if no medium loaded - let file = open_linux_tape_device(&drive_config.path) - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, drive_config.path, err))?; + let file = open_linux_tape_device(&drive_config.path)?; let mut handle = LinuxTapeHandle::new(file); diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs index 59b2433f..71875324 100644 --- a/src/api2/types/mod.rs +++ b/src/api2/types/mod.rs @@ -77,7 +77,7 @@ const_regex!{ pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$"); - pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$"; + pub FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$"; pub ACL_PATH_REGEX = concat!(r"^(?:/|", r"(?:/", PROXMOX_SAFE_ID_REGEX_STR!(), ")+", r")$"); @@ -103,8 +103,8 @@ pub const IP_FORMAT: ApiStringFormat = pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); -pub const CERT_FINGERPRINT_SHA256_FORMAT: ApiStringFormat = - ApiStringFormat::Pattern(&CERT_FINGERPRINT_SHA256_REGEX); +pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat = + ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX); pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX); @@ -163,17 +163,22 @@ pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.") pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema = StringSchema::new( "X509 certificate fingerprint (sha256)." ) - .format(&CERT_FINGERPRINT_SHA256_FORMAT) + .format(&FINGERPRINT_SHA256_FORMAT) .schema(); -pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(r#"\ -Prevent changes if current configuration file has different SHA256 digest. -This can be used to prevent concurrent modifications. -"# +pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema = StringSchema::new( + "Tape encryption key fingerprint (sha256)." ) - .format(&PVE_CONFIG_DIGEST_FORMAT) + .format(&FINGERPRINT_SHA256_FORMAT) .schema(); +pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new( + "Prevent changes if current configuration file has different \ + SHA256 digest. This can be used to prevent concurrent \ + modifications." +) + .format(&PVE_CONFIG_DIGEST_FORMAT) .schema(); + pub const CHUNK_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); diff --git a/src/api2/types/tape/drive.rs b/src/api2/types/tape/drive.rs index d1d09ed1..f90d22ba 100644 --- a/src/api2/types/tape/drive.rs +++ b/src/api2/types/tape/drive.rs @@ -12,7 +12,8 @@ use proxmox::api::{ use crate::api2::types::{ PROXMOX_SAFE_ID_FORMAT, CHANGER_NAME_SCHEMA, -}; + CERT_FINGERPRINT_SHA256_SCHEMA, + }; pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.") .format(&PROXMOX_SAFE_ID_FORMAT) @@ -205,3 +206,18 @@ pub struct LinuxDriveAndMediaStatus { #[serde(skip_serializing_if="Option::is_none")] pub medium_passes: Option, } + +#[api( + properties: { + fingerprint: { + schema: CERT_FINGERPRINT_SHA256_SCHEMA, + }, + }, +)] +#[derive(Deserialize, Serialize)] +/// Hardware Encryption key Metadata +pub struct TapeKeyMetadata { + /// Password hint + pub hint: String, + pub fingerprint: String, +} diff --git a/src/backup/crypt_config.rs b/src/backup/crypt_config.rs index 2b04380a..92601f61 100644 --- a/src/backup/crypt_config.rs +++ b/src/backup/crypt_config.rs @@ -39,7 +39,7 @@ pub enum CryptMode { SignOnly, } -#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)] #[serde(transparent)] /// 32-byte fingerprint, usually calculated with SHA256. pub struct Fingerprint { diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs index 05f82a86..3ac1337e 100644 --- a/src/bin/proxmox-tape.rs +++ b/src/bin/proxmox-tape.rs @@ -952,6 +952,7 @@ fn main() { .insert("drive", drive_commands()) .insert("pool", pool_commands()) .insert("media", media_commands()) + .insert("key", encryption_key_commands()) .insert( "load-media", CliCommand::new(&API_METHOD_LOAD_MEDIA) diff --git a/src/bin/proxmox_tape/encryption_key.rs b/src/bin/proxmox_tape/encryption_key.rs new file mode 100644 index 00000000..78c2dce7 --- /dev/null +++ b/src/bin/proxmox_tape/encryption_key.rs @@ -0,0 +1,70 @@ +use anyhow::Error; +use serde_json::Value; + +use proxmox::{ + api::{ + api, + cli::*, + RpcEnvironment, + ApiHandler, + }, +}; + +use proxmox_backup::{ + api2::{ + self, + }, + config::tape_encryption_keys::complete_key_fingerprint, +}; + +pub fn encryption_key_commands() -> CommandLineInterface { + + let cmd_def = CliCommandMap::new() + .insert("list", CliCommand::new(&API_METHOD_LIST_KEYS)) + .insert( + "create", + CliCommand::new(&api2::config::tape_encryption_keys::API_METHOD_CREATE_KEY) + ) + .insert( + "remove", + CliCommand::new(&api2::config::tape_encryption_keys::API_METHOD_DELETE_KEY) + .arg_param(&["fingerprint"]) + .completion_cb("fingerprint", complete_key_fingerprint) + ) + ; + + cmd_def.into() +} + +#[api( + input: { + properties: { + "output-format": { + schema: OUTPUT_FORMAT, + optional: true, + }, + }, + }, +)] +/// List keys +fn list_keys( + param: Value, + rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let output_format = get_output_format(¶m); + let info = &api2::config::tape_encryption_keys::API_METHOD_LIST_KEYS; + let mut data = match info.handler { + ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?, + _ => unreachable!(), + }; + + let options = default_table_format_options() + .column(ColumnConfig::new("fingerprint")) + .column(ColumnConfig::new("hint")) + ; + + format_and_print_result_full(&mut data, &info.returns, &output_format, &options); + + Ok(()) +} diff --git a/src/bin/proxmox_tape/mod.rs b/src/bin/proxmox_tape/mod.rs index 04e6cf50..5e04d901 100644 --- a/src/bin/proxmox_tape/mod.rs +++ b/src/bin/proxmox_tape/mod.rs @@ -9,3 +9,6 @@ pub use pool::*; mod media; pub use media::*; + +mod encryption_key; +pub use encryption_key::*; diff --git a/src/bin/sg-tape-cmd.rs b/src/bin/sg-tape-cmd.rs index c86ac6e6..4ced4194 100644 --- a/src/bin/sg-tape-cmd.rs +++ b/src/bin/sg-tape-cmd.rs @@ -21,9 +21,11 @@ use proxmox::{ use proxmox_backup::{ config, + backup::Fingerprint, api2::types::{ LINUX_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, + TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, LinuxTapeDrive, }, tape::{ @@ -42,8 +44,7 @@ fn get_tape_handle(param: &Value) -> Result { let (config, _digest) = config::drive::config()?; let drive: LinuxTapeDrive = config.lookup("linux", &name)?; eprintln!("using device {}", drive.path); - drive.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", name, drive.path, err))? + drive.open()? } else if let Some(device) = param["device"].as_str() { eprintln!("using device {}", device); LinuxTapeHandle::new(open_linux_tape_device(&device)?) @@ -57,8 +58,7 @@ fn get_tape_handle(param: &Value) -> Result { let (config, _digest) = config::drive::config()?; let drive: LinuxTapeDrive = config.lookup("linux", &name)?; eprintln!("using device {}", drive.path); - drive.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", name, drive.path, err))? + drive.open()? } else { let (config, _digest) = config::drive::config()?; @@ -72,8 +72,7 @@ fn get_tape_handle(param: &Value) -> Result { let name = drive_names[0]; let drive: LinuxTapeDrive = config.lookup("linux", &name)?; eprintln!("using device {}", drive.path); - drive.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", name, drive.path, err))? + drive.open()? } else { bail!("no drive/device specified"); } @@ -187,6 +186,47 @@ fn tape_alert_flags( Ok(()) } +#[api( + input: { + properties: { + fingerprint: { + schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + optional: true, + }, + drive: { + schema: DRIVE_NAME_SCHEMA, + optional: true, + }, + device: { + schema: LINUX_DRIVE_PATH_SCHEMA, + optional: true, + }, + stdin: { + description: "Use standard input as device handle.", + type: bool, + optional: true, + }, + }, + }, +)] +/// Set or clear encryption key +fn set_encryption( + fingerprint: Option, + param: Value, +) -> Result<(), Error> { + + let result = proxmox::try_block!({ + let mut handle = get_tape_handle(¶m)?; + + handle.set_encryption(fingerprint)?; + Ok(()) + }).map_err(|err: Error| err.to_string()); + + println!("{}", serde_json::to_string_pretty(&result)?); + + Ok(()) +} + #[api( input: { properties: { @@ -260,6 +300,10 @@ fn main() -> Result<(), Error> { "volume-statistics", CliCommand::new(&API_METHOD_VOLUME_STATISTICS) ) + .insert( + "encryption", + CliCommand::new(&API_METHOD_SET_ENCRYPTION) + ) ; let mut rpcenv = CliEnvironment::new(); diff --git a/src/config.rs b/src/config.rs index 75a9faa6..c1b91b39 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,6 +27,7 @@ pub mod user; pub mod verify; pub mod drive; pub mod media_pool; +pub mod tape_encryption_keys; /// Check configuration directory permissions /// diff --git a/src/config/tape_encryption_keys.rs b/src/config/tape_encryption_keys.rs new file mode 100644 index 00000000..854c330f --- /dev/null +++ b/src/config/tape_encryption_keys.rs @@ -0,0 +1,126 @@ +use std::collections::HashMap; + +use anyhow::{bail, Error}; +use serde::{Deserialize, Serialize}; +use openssl::sha::sha256; + +use proxmox::tools::fs::{ + file_read_optional_string, + replace_file, + CreateOptions, +}; + +use crate::{ + backup::{ + Fingerprint, + }, +}; + +mod hex_key { + use serde::{self, Deserialize, Serializer, Deserializer}; + + pub fn serialize( + csum: &[u8; 32], + serializer: S, + ) -> Result + where + S: Serializer, + { + let s = proxmox::tools::digest_to_hex(csum); + serializer.serialize_str(&s) + } + + pub fn deserialize<'de, D>( + deserializer: D, + ) -> Result<[u8; 32], D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + proxmox::tools::hex_to_digest(&s).map_err(serde::de::Error::custom) + } +} + +/// Store Hardware Encryption keys +#[derive(Deserialize, Serialize)] +pub struct EncryptionKeyInfo { + pub hint: String, + #[serde(with = "hex_key")] + pub key: [u8; 32], + pub fingerprint: Fingerprint, +} + +impl EncryptionKeyInfo { + + pub fn new(key: &[u8; 32], hint: String) -> Self { + Self { + hint, + key: key.clone(), + fingerprint: Fingerprint::new(sha256(key)), + } + } +} + +pub const TAPE_KEYS_FILENAME: &str = "/etc/proxmox-backup/tape-encryption-keys.json"; +pub const TAPE_KEYS_LOCKFILE: &str = "/etc/proxmox-backup/.tape-encryption-keys.lck"; + +pub fn load_keys() -> Result<(HashMap, [u8;32]), Error> { + + let content = file_read_optional_string(TAPE_KEYS_FILENAME)?; + let content = content.unwrap_or_else(|| String::from("[]")); + + let digest = openssl::sha::sha256(content.as_bytes()); + + let list: Vec = serde_json::from_str(&content)?; + + let mut map = HashMap::new(); + + for item in list { + let expected_fingerprint = Fingerprint::new(sha256(&item.key)); + if item.fingerprint != expected_fingerprint { + bail!( + "inconsistent fingerprint ({} != {})", + item.fingerprint, + expected_fingerprint, + ); + } + + map.insert(item.fingerprint.clone(), item); + } + + Ok((map, digest)) +} + +pub fn save_keys(map: HashMap) -> Result<(), Error> { + + let mut list = Vec::new(); + + for (_fp, item) in map { + list.push(item); + } + + let raw = serde_json::to_string_pretty(&list)?; + + let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600); + // set the correct owner/group/permissions while saving file + // owner(rw) = root, group(r)= root + let options = CreateOptions::new() + .perm(mode) + .owner(nix::unistd::ROOT) + .group(nix::unistd::Gid::from_raw(0)); + + replace_file(TAPE_KEYS_FILENAME, raw.as_bytes(), options)?; + + Ok(()) +} + +// shell completion helper +pub fn complete_key_fingerprint(_arg: &str, _param: &HashMap) -> Vec { + let data = match load_keys() { + Ok((data, _digest)) => data, + Err(_) => return Vec::new(), + }; + + data.keys().map(|fp| crate::tools::format::as_fingerprint(fp.bytes())).collect() +} + diff --git a/src/tape/drive/linux_tape.rs b/src/tape/drive/linux_tape.rs index 7ed18adf..4e75a073 100644 --- a/src/tape/drive/linux_tape.rs +++ b/src/tape/drive/linux_tape.rs @@ -9,6 +9,8 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag}; use proxmox::sys::error::SysResult; use crate::{ + config, + backup::Fingerprint, tools::run_command, api2::types::{ TapeDensity, @@ -24,6 +26,7 @@ use crate::{ mam_extract_media_usage, read_tape_alert_flags, read_volume_statistics, + set_encryption, drive::{ LinuxTapeDrive, TapeDriver, @@ -504,6 +507,43 @@ impl TapeDriver for LinuxTapeHandle { .map_err(|err| format_err!("{}", err)) .map(|bits| TapeAlertFlags::from_bits_truncate(bits)) } + + /// Set or clear encryption key + /// + /// Note: Only 'root' user may run RAW SG commands, so we need to + /// spawn setuid binary 'sg-tape-cmd'. Also, encryption key file + /// is only readable by root. + fn set_encryption(&mut self, key_fingerprint: Option) -> Result<(), Error> { + + if nix::unistd::Uid::effective().is_root() { + + if let Some(ref key_fingerprint) = key_fingerprint { + + let (key_map, _digest) = config::tape_encryption_keys::load_keys()?; + match key_map.get(key_fingerprint) { + Some(item) => { + return set_encryption(&mut self.file, Some(item.key)); + } + None => bail!("unknown tape encryption key '{}'", key_fingerprint), + } + } else { + return set_encryption(&mut self.file, None); + } + } + + let mut command = std::process::Command::new( + "/usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd"); + command.args(&["encryption"]); + if let Some(fingerprint) = key_fingerprint { + let fingerprint = crate::tools::format::as_fingerprint(fingerprint.bytes()); + command.args(&["--fingerprint", &fingerprint]); + } + command.args(&["--stdin"]); + command.stdin(unsafe { std::process::Stdio::from_raw_fd(self.file.as_raw_fd())}); + let output = run_command(command, None)?; + let result: Result<(), String> = serde_json::from_str(&output)?; + result.map_err(|err| format_err!("{}", err)) + } } /// Write a single EOF mark without flushing buffers diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs index 5c710bde..8fa39d01 100644 --- a/src/tape/drive/mod.rs +++ b/src/tape/drive/mod.rs @@ -25,6 +25,7 @@ use proxmox::tools::io::ReadExt; use proxmox::api::section_config::SectionConfigData; use crate::{ + backup::Fingerprint, api2::types::{ VirtualTapeDrive, LinuxTapeDrive, @@ -163,6 +164,14 @@ pub trait TapeDriver { fn tape_alert_flags(&mut self) -> Result { Ok(TapeAlertFlags::empty()) } + + /// Set or clear encryption key + fn set_encryption(&mut self, key_fingerprint: Option) -> Result<(), Error> { + if key_fingerprint.is_some() { + bail!("drive does not support encryption"); + } + Ok(()) + } } /// Get the media changer (MediaChange + name) associated with a tape drive. @@ -234,14 +243,12 @@ pub fn open_drive( match section_type_name.as_ref() { "virtual" => { let tape = VirtualTapeDrive::deserialize(config)?; - let handle = tape.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, tape.path, err))?; - Ok(Box::new(handle)) + let handle = tape.open()?; + Ok(Box::new(handle)) } "linux" => { let tape = LinuxTapeDrive::deserialize(config)?; - let handle = tape.open() - .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, tape.path, err))?; + let handle = tape.open()?; Ok(Box::new(handle)) } _ => bail!("unknown drive type '{}' - internal error"), diff --git a/src/tape/drive/virtual_tape.rs b/src/tape/drive/virtual_tape.rs index f578b298..f2741109 100644 --- a/src/tape/drive/virtual_tape.rs +++ b/src/tape/drive/virtual_tape.rs @@ -40,18 +40,20 @@ impl VirtualTapeDrive { /// This needs to lock the drive pub fn open(&self) -> Result { - let mut lock_path = std::path::PathBuf::from(&self.path); - lock_path.push(".drive.lck"); + proxmox::try_block!({ + let mut lock_path = std::path::PathBuf::from(&self.path); + lock_path.push(".drive.lck"); - let timeout = std::time::Duration::new(10, 0); - let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true)?; + let timeout = std::time::Duration::new(10, 0); + let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true)?; - Ok(VirtualTapeHandle { - _lock: lock, - drive_name: self.name.clone(), - max_size: self.max_size.unwrap_or(64*1024*1024), - path: std::path::PathBuf::from(&self.path), - }) + Ok(VirtualTapeHandle { + _lock: lock, + drive_name: self.name.clone(), + max_size: self.max_size.unwrap_or(64*1024*1024), + path: std::path::PathBuf::from(&self.path), + }) + }).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", self.name, self.path, err)) } }