backup client: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
@ -13,32 +13,25 @@ use nix::unistd::{fork, ForkResult};
|
||||
use serde_json::Value;
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
|
||||
use proxmox_sys::sortable;
|
||||
use proxmox_sys::fd::Fd;
|
||||
use proxmox_router::{ApiHandler, ApiMethod, RpcEnvironment, cli::*};
|
||||
use proxmox_router::{cli::*, ApiHandler, ApiMethod, RpcEnvironment};
|
||||
use proxmox_schema::*;
|
||||
use proxmox_sys::fd::Fd;
|
||||
use proxmox_sys::sortable;
|
||||
|
||||
use pbs_tools::crypt_config::CryptConfig;
|
||||
use pbs_config::key_config::load_and_decrypt_key;
|
||||
use pbs_datastore::{BackupDir, BackupGroup, };
|
||||
use pbs_datastore::index::IndexFile;
|
||||
use pbs_datastore::dynamic_index::BufferedDynamicReader;
|
||||
use pbs_datastore::cached_chunk_reader::CachedChunkReader;
|
||||
use pbs_client::tools::key_source::get_encryption_key_password;
|
||||
use pbs_client::{BackupReader, RemoteChunkReader};
|
||||
use pbs_config::key_config::load_and_decrypt_key;
|
||||
use pbs_datastore::cached_chunk_reader::CachedChunkReader;
|
||||
use pbs_datastore::dynamic_index::BufferedDynamicReader;
|
||||
use pbs_datastore::index::IndexFile;
|
||||
use pbs_datastore::{BackupDir, BackupGroup};
|
||||
use pbs_tools::crypt_config::CryptConfig;
|
||||
use pbs_tools::json::required_string_param;
|
||||
|
||||
use crate::{
|
||||
REPO_URL_SCHEMA,
|
||||
extract_repository_from_value,
|
||||
complete_pxar_archive_name,
|
||||
complete_img_archive_name,
|
||||
complete_group_or_snapshot,
|
||||
complete_repository,
|
||||
record_repository,
|
||||
connect,
|
||||
api_datastore_latest_snapshot,
|
||||
BufferedDynamicReadAt,
|
||||
api_datastore_latest_snapshot, complete_group_or_snapshot, complete_img_archive_name,
|
||||
complete_pxar_archive_name, complete_repository, connect, extract_repository_from_value,
|
||||
record_repository, BufferedDynamicReadAt, REPO_URL_SCHEMA,
|
||||
};
|
||||
|
||||
#[sortable]
|
||||
@ -47,14 +40,36 @@ const API_METHOD_MOUNT: ApiMethod = ApiMethod::new(
|
||||
&ObjectSchema::new(
|
||||
"Mount pxar archive.",
|
||||
&sorted!([
|
||||
("snapshot", false, &StringSchema::new("Group/Snapshot path.").schema()),
|
||||
("archive-name", false, &StringSchema::new("Backup archive name.").schema()),
|
||||
("target", false, &StringSchema::new("Target directory path.").schema()),
|
||||
(
|
||||
"snapshot",
|
||||
false,
|
||||
&StringSchema::new("Group/Snapshot path.").schema()
|
||||
),
|
||||
(
|
||||
"archive-name",
|
||||
false,
|
||||
&StringSchema::new("Backup archive name.").schema()
|
||||
),
|
||||
(
|
||||
"target",
|
||||
false,
|
||||
&StringSchema::new("Target directory path.").schema()
|
||||
),
|
||||
("repository", true, &REPO_URL_SCHEMA),
|
||||
("keyfile", true, &StringSchema::new("Path to encryption key.").schema()),
|
||||
("verbose", true, &BooleanSchema::new("Verbose output and stay in foreground.").default(false).schema()),
|
||||
(
|
||||
"keyfile",
|
||||
true,
|
||||
&StringSchema::new("Path to encryption key.").schema()
|
||||
),
|
||||
(
|
||||
"verbose",
|
||||
true,
|
||||
&BooleanSchema::new("Verbose output and stay in foreground.")
|
||||
.default(false)
|
||||
.schema()
|
||||
),
|
||||
]),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
#[sortable]
|
||||
@ -64,13 +79,31 @@ const API_METHOD_MAP: ApiMethod = ApiMethod::new(
|
||||
"Map a drive image from a VM backup to a local loopback device. Use 'unmap' to undo.
|
||||
WARNING: Only do this with *trusted* backups!",
|
||||
&sorted!([
|
||||
("snapshot", false, &StringSchema::new("Group/Snapshot path.").schema()),
|
||||
("archive-name", false, &StringSchema::new("Backup archive name.").schema()),
|
||||
(
|
||||
"snapshot",
|
||||
false,
|
||||
&StringSchema::new("Group/Snapshot path.").schema()
|
||||
),
|
||||
(
|
||||
"archive-name",
|
||||
false,
|
||||
&StringSchema::new("Backup archive name.").schema()
|
||||
),
|
||||
("repository", true, &REPO_URL_SCHEMA),
|
||||
("keyfile", true, &StringSchema::new("Path to encryption key.").schema()),
|
||||
("verbose", true, &BooleanSchema::new("Verbose output and stay in foreground.").default(false).schema()),
|
||||
(
|
||||
"keyfile",
|
||||
true,
|
||||
&StringSchema::new("Path to encryption key.").schema()
|
||||
),
|
||||
(
|
||||
"verbose",
|
||||
true,
|
||||
&BooleanSchema::new("Verbose output and stay in foreground.")
|
||||
.default(false)
|
||||
.schema()
|
||||
),
|
||||
]),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
#[sortable]
|
||||
@ -78,17 +111,19 @@ const API_METHOD_UNMAP: ApiMethod = ApiMethod::new(
|
||||
&ApiHandler::Sync(&unmap),
|
||||
&ObjectSchema::new(
|
||||
"Unmap a loop device mapped with 'map' and release all resources.",
|
||||
&sorted!([
|
||||
("name", true, &StringSchema::new(
|
||||
concat!("Archive name, path to loopdev (/dev/loopX) or loop device number. ",
|
||||
"Omit to list all current mappings and force cleaning up leftover instances.")
|
||||
).schema()),
|
||||
]),
|
||||
)
|
||||
&sorted!([(
|
||||
"name",
|
||||
true,
|
||||
&StringSchema::new(concat!(
|
||||
"Archive name, path to loopdev (/dev/loopX) or loop device number. ",
|
||||
"Omit to list all current mappings and force cleaning up leftover instances."
|
||||
))
|
||||
.schema()
|
||||
),]),
|
||||
),
|
||||
);
|
||||
|
||||
pub fn mount_cmd_def() -> CliCommand {
|
||||
|
||||
CliCommand::new(&API_METHOD_MOUNT)
|
||||
.arg_param(&["snapshot", "archive-name", "target"])
|
||||
.completion_cb("repository", complete_repository)
|
||||
@ -98,7 +133,6 @@ pub fn mount_cmd_def() -> CliCommand {
|
||||
}
|
||||
|
||||
pub fn map_cmd_def() -> CliCommand {
|
||||
|
||||
CliCommand::new(&API_METHOD_MAP)
|
||||
.arg_param(&["snapshot", "archive-name"])
|
||||
.completion_cb("repository", complete_repository)
|
||||
@ -107,21 +141,20 @@ pub fn map_cmd_def() -> CliCommand {
|
||||
}
|
||||
|
||||
pub fn unmap_cmd_def() -> CliCommand {
|
||||
|
||||
CliCommand::new(&API_METHOD_UNMAP)
|
||||
.arg_param(&["name"])
|
||||
.completion_cb("name", complete_mapping_names)
|
||||
}
|
||||
|
||||
fn complete_mapping_names<S: BuildHasher>(_arg: &str, _param: &HashMap<String, String, S>)
|
||||
-> Vec<String>
|
||||
{
|
||||
fn complete_mapping_names<S: BuildHasher>(
|
||||
_arg: &str,
|
||||
_param: &HashMap<String, String, S>,
|
||||
) -> Vec<String> {
|
||||
match pbs_fuse_loop::find_all_mappings() {
|
||||
Ok(mappings) => mappings
|
||||
.filter_map(|(name, _)| {
|
||||
proxmox_sys::systemd::unescape_unit(&name).ok()
|
||||
}).collect(),
|
||||
Err(_) => Vec::new()
|
||||
.filter_map(|(name, _)| proxmox_sys::systemd::unescape_unit(&name).ok())
|
||||
.collect(),
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +163,6 @@ fn mount(
|
||||
_info: &ApiMethod,
|
||||
_rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
|
||||
let verbose = param["verbose"].as_bool().unwrap_or(false);
|
||||
if verbose {
|
||||
// This will stay in foreground with debug output enabled as None is
|
||||
@ -172,7 +204,11 @@ async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
||||
api_datastore_latest_snapshot(&client, repo.store(), group).await?
|
||||
} else {
|
||||
let snapshot: BackupDir = path.parse()?;
|
||||
(snapshot.group().backup_type().to_owned(), snapshot.group().backup_id().to_owned(), snapshot.backup_time())
|
||||
(
|
||||
snapshot.group().backup_type().to_owned(),
|
||||
snapshot.group().backup_id().to_owned(),
|
||||
snapshot.backup_time(),
|
||||
)
|
||||
};
|
||||
|
||||
let keyfile = param["keyfile"].as_str().map(PathBuf::from);
|
||||
@ -208,7 +244,8 @@ async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
||||
&backup_id,
|
||||
backup_time,
|
||||
true,
|
||||
).await?;
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (manifest, _) = client.download_manifest().await?;
|
||||
manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
|
||||
@ -223,7 +260,8 @@ async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
||||
"/dev/null",
|
||||
nix::fcntl::OFlag::O_RDWR,
|
||||
nix::sys::stat::Mode::empty(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
nix::unistd::dup2(nullfd, 0).unwrap();
|
||||
nix::unistd::dup2(nullfd, 1).unwrap();
|
||||
nix::unistd::dup2(nullfd, 2).unwrap();
|
||||
@ -245,16 +283,23 @@ async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
||||
let mut interrupt_int = signal(SignalKind::interrupt())?;
|
||||
let mut interrupt_term = signal(SignalKind::terminate())?;
|
||||
|
||||
let mut interrupt = futures::future::select(interrupt_int.recv().boxed(), interrupt_term.recv().boxed());
|
||||
let mut interrupt =
|
||||
futures::future::select(interrupt_int.recv().boxed(), interrupt_term.recv().boxed());
|
||||
|
||||
if server_archive_name.ends_with(".didx") {
|
||||
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
|
||||
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, file_info.chunk_crypt_mode(), most_used);
|
||||
let chunk_reader = RemoteChunkReader::new(
|
||||
client.clone(),
|
||||
crypt_config,
|
||||
file_info.chunk_crypt_mode(),
|
||||
most_used,
|
||||
);
|
||||
let reader = BufferedDynamicReader::new(index, chunk_reader);
|
||||
let archive_size = reader.archive_size();
|
||||
let reader: pbs_client::pxar::fuse::Reader =
|
||||
Arc::new(BufferedDynamicReadAt::new(reader));
|
||||
let reader: pbs_client::pxar::fuse::Reader = Arc::new(BufferedDynamicReadAt::new(reader));
|
||||
let decoder = pbs_client::pxar::fuse::Accessor::new(reader, archive_size).await?;
|
||||
|
||||
let session = pbs_client::pxar::fuse::Session::mount(
|
||||
@ -274,15 +319,23 @@ async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
||||
}
|
||||
}
|
||||
} else if server_archive_name.ends_with(".fidx") {
|
||||
let index = client.download_fixed_index(&manifest, &server_archive_name).await?;
|
||||
let index = client
|
||||
.download_fixed_index(&manifest, &server_archive_name)
|
||||
.await?;
|
||||
let size = index.index_bytes();
|
||||
let chunk_reader = RemoteChunkReader::new(client.clone(), crypt_config, file_info.chunk_crypt_mode(), HashMap::new());
|
||||
let chunk_reader = RemoteChunkReader::new(
|
||||
client.clone(),
|
||||
crypt_config,
|
||||
file_info.chunk_crypt_mode(),
|
||||
HashMap::new(),
|
||||
);
|
||||
let reader = CachedChunkReader::new(chunk_reader, index, 8).seekable();
|
||||
|
||||
let name = &format!("{}:{}/{}", repo, path, archive_name);
|
||||
let name_escaped = proxmox_sys::systemd::escape_unit(name, false);
|
||||
|
||||
let mut session = pbs_fuse_loop::FuseLoopSession::map_loop(size, reader, &name_escaped, options).await?;
|
||||
let mut session =
|
||||
pbs_fuse_loop::FuseLoopSession::map_loop(size, reader, &name_escaped, options).await?;
|
||||
let loopdev = session.loopdev_path.clone();
|
||||
|
||||
let (st_send, st_recv) = futures::channel::mpsc::channel(1);
|
||||
@ -335,7 +388,6 @@ fn unmap(
|
||||
_info: &ApiMethod,
|
||||
_rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
|
||||
let mut name = match param["name"].as_str() {
|
||||
Some(name) => name.to_owned(),
|
||||
None => {
|
||||
@ -343,14 +395,18 @@ fn unmap(
|
||||
let mut any = false;
|
||||
for (backing, loopdev) in pbs_fuse_loop::find_all_mappings()? {
|
||||
let name = proxmox_sys::systemd::unescape_unit(&backing)?;
|
||||
println!("{}:\t{}", loopdev.unwrap_or_else(|| "(unmapped)".to_string()), name);
|
||||
println!(
|
||||
"{}:\t{}",
|
||||
loopdev.unwrap_or_else(|| "(unmapped)".to_string()),
|
||||
name
|
||||
);
|
||||
any = true;
|
||||
}
|
||||
if !any {
|
||||
println!("Nothing mapped.");
|
||||
}
|
||||
return Ok(Value::Null);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// allow loop device number alone
|
||||
|
Reference in New Issue
Block a user