use std::os::unix::fs::OpenOptionsExt;
use std::io::{Seek, SeekFrom};
-use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{bail, format_err, Error};
use proxmox::api::{api, cli::*};
-use proxmox_backup::tools;
-
-use proxmox_backup::client::*;
+use pbs_client::tools::key_source::get_encryption_key_password;
+use pbs_client::{BackupReader, RemoteChunkReader};
+use pbs_tools::json::required_string_param;
use crate::{
REPO_URL_SCHEMA,
+ KEYFD_SCHEMA,
extract_repository_from_value,
+ format_key_source,
record_repository,
- load_and_decrypt_key,
+ decrypt_key,
api_datastore_latest_snapshot,
complete_repository,
complete_backup_snapshot,
complete_group_or_snapshot,
complete_pxar_archive_name,
connect,
+ crypto_parameters,
BackupDir,
BackupGroup,
BufferedDynamicReader,
Shell,
};
-use crate::key::get_encryption_key_password;
-
#[api(
input: {
properties: {
type: String,
description: "Snapshot path.",
},
+ "keyfile": {
+ optional: true,
+ type: String,
+ description: "Path to encryption key.",
+ },
+ "keyfd": {
+ schema: KEYFD_SCHEMA,
+ optional: true,
+ },
}
}
)]
let repo = extract_repository_from_value(¶m)?;
- let path = tools::required_string_param(¶m, "snapshot")?;
+ let path = required_string_param(¶m, "snapshot")?;
let snapshot: BackupDir = path.parse()?;
- let keyfile = param["keyfile"].as_str().map(PathBuf::from);
+ let crypto = crypto_parameters(¶m)?;
- let crypt_config = match keyfile {
+ let crypt_config = match crypto.enc_key {
None => None,
- Some(path) => {
- let (key, _) = load_and_decrypt_key(&path, &get_encryption_key_password)?;
- Some(Arc::new(CryptConfig::new(key)?))
+ Some(key) => {
+ let (key, _created, _fingerprint) = decrypt_key(&key.key, &get_encryption_key_password)
+ .map_err(|err| {
+ eprintln!("{}", format_key_source(&key.source, "encryption"));
+ err
+ })?;
+ let crypt_config = CryptConfig::new(key)?;
+ Some(Arc::new(crypt_config))
}
};
- let client = connect(repo.host(), repo.user())?;
+ let client = connect(&repo)?;
let client = BackupReader::start(
client,
true,
).await?;
- let manifest = client.download_manifest().await?;
+ let (manifest, _) = client.download_manifest().await?;
+ manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
let index = client.download_dynamic_index(&manifest, CATALOG_NAME).await?;
let most_used = index.find_most_used_chunks(8);
- let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, most_used);
+ let file_info = manifest.lookup_file_info(&CATALOG_NAME)?;
+
+ let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, file_info.chunk_crypt_mode(), most_used);
let mut reader = BufferedDynamicReader::new(index, chunk_reader);
type: String,
description: "Path to encryption key.",
},
- },
+ "keyfd": {
+ schema: KEYFD_SCHEMA,
+ optional: true,
+ },
+ },
},
)]
/// Shell to interactively inspect and restore snapshots.
async fn catalog_shell(param: Value) -> Result<(), Error> {
let repo = extract_repository_from_value(¶m)?;
- let client = connect(repo.host(), repo.user())?;
- let path = tools::required_string_param(¶m, "snapshot")?;
- let archive_name = tools::required_string_param(¶m, "archive-name")?;
+ let client = connect(&repo)?;
+ let path = required_string_param(¶m, "snapshot")?;
+ let archive_name = required_string_param(¶m, "archive-name")?;
let (backup_type, backup_id, backup_time) = if path.matches('/').count() == 1 {
let group: BackupGroup = path.parse()?;
(snapshot.group().backup_type().to_owned(), snapshot.group().backup_id().to_owned(), snapshot.backup_time())
};
- let keyfile = param["keyfile"].as_str().map(|p| PathBuf::from(p));
- let crypt_config = match keyfile {
+ let crypto = crypto_parameters(¶m)?;
+
+ let crypt_config = match crypto.enc_key {
None => None,
- Some(path) => {
- let (key, _) = load_and_decrypt_key(&path, &get_encryption_key_password)?;
- Some(Arc::new(CryptConfig::new(key)?))
+ Some(key) => {
+ let (key, _created, _fingerprint) = decrypt_key(&key.key, &get_encryption_key_password)
+ .map_err(|err| {
+ eprintln!("{}", format_key_source(&key.source, "encryption"));
+ err
+ })?;
+ let crypt_config = CryptConfig::new(key)?;
+ Some(Arc::new(crypt_config))
}
};
.custom_flags(libc::O_TMPFILE)
.open("/tmp")?;
- let manifest = client.download_manifest().await?;
+ let (manifest, _) = client.download_manifest().await?;
+ manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
let most_used = index.find_most_used_chunks(8);
- let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config.clone(), most_used);
+
+ let file_info = manifest.lookup_file_info(&server_archive_name)?;
+ let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config.clone(), file_info.chunk_crypt_mode(), most_used);
let reader = BufferedDynamicReader::new(index, chunk_reader);
let archive_size = reader.archive_size();
- let reader: proxmox_backup::pxar::fuse::Reader =
+ let reader: pbs_client::pxar::fuse::Reader =
Arc::new(BufferedDynamicReadAt::new(reader));
- let decoder = proxmox_backup::pxar::fuse::Accessor::new(reader, archive_size).await?;
+ let decoder = pbs_client::pxar::fuse::Accessor::new(reader, archive_size).await?;
client.download(CATALOG_NAME, &mut tmpfile).await?;
let index = DynamicIndexReader::new(tmpfile)
manifest.verify_file(CATALOG_NAME, &csum, size)?;
let most_used = index.find_most_used_chunks(8);
- let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, most_used);
+
+ let file_info = manifest.lookup_file_info(&CATALOG_NAME)?;
+ let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, file_info.chunk_crypt_mode(), most_used);
let mut reader = BufferedDynamicReader::new(index, chunk_reader);
let mut catalogfile = std::fs::OpenOptions::new()
.write(true)