client: rustfmt
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
@ -1,14 +1,14 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use std::io::Read;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde_json::Value;
|
||||
|
||||
use proxmox_sys::linux::tty;
|
||||
use proxmox_sys::fs::file_get_contents;
|
||||
use proxmox_schema::*;
|
||||
use proxmox_sys::fs::file_get_contents;
|
||||
use proxmox_sys::linux::tty;
|
||||
|
||||
use pbs_api_types::CryptMode;
|
||||
|
||||
@ -102,11 +102,12 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
|
||||
|
||||
let key_fd = match param.get("keyfd") {
|
||||
Some(Value::Number(key_fd)) => Some(
|
||||
RawFd::try_from(key_fd
|
||||
.as_i64()
|
||||
.ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?
|
||||
RawFd::try_from(
|
||||
key_fd
|
||||
.as_i64()
|
||||
.ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?,
|
||||
)
|
||||
.map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?
|
||||
.map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?,
|
||||
),
|
||||
Some(_) => bail!("bad --keyfd parameter type"),
|
||||
None => None,
|
||||
@ -120,11 +121,12 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
|
||||
|
||||
let master_pubkey_fd = match param.get("master-pubkey-fd") {
|
||||
Some(Value::Number(key_fd)) => Some(
|
||||
RawFd::try_from(key_fd
|
||||
.as_i64()
|
||||
.ok_or_else(|| format_err!("bad master public key fd: {:?}", key_fd))?
|
||||
RawFd::try_from(
|
||||
key_fd
|
||||
.as_i64()
|
||||
.ok_or_else(|| format_err!("bad master public key fd: {:?}", key_fd))?,
|
||||
)
|
||||
.map_err(|err| format_err!("bad public master key fd: {:?}: {}", key_fd, err))?
|
||||
.map_err(|err| format_err!("bad public master key fd: {:?}: {}", key_fd, err))?,
|
||||
),
|
||||
Some(_) => bail!("bad --master-pubkey-fd parameter type"),
|
||||
None => None,
|
||||
@ -151,7 +153,9 @@ fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoPa
|
||||
if keep_keyfd_open {
|
||||
// don't close fd if requested, and try to reset seek position
|
||||
std::mem::forget(input);
|
||||
unsafe { libc::lseek(fd, 0, libc::SEEK_SET); }
|
||||
unsafe {
|
||||
libc::lseek(fd, 0, libc::SEEK_SET);
|
||||
}
|
||||
}
|
||||
Some(KeyWithSource::from_fd(data))
|
||||
}
|
||||
@ -373,14 +377,14 @@ fn create_testdir(name: &str) -> Result<String, Error> {
|
||||
// WARNING: there must only be one test for crypto_parameters as the default key handling is not
|
||||
// safe w.r.t. concurrency
|
||||
fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
use serde_json::json;
|
||||
use proxmox_sys::fs::{replace_file, CreateOptions};
|
||||
use serde_json::json;
|
||||
|
||||
let some_key = vec![1;1];
|
||||
let default_key = vec![2;1];
|
||||
let some_key = vec![1; 1];
|
||||
let default_key = vec![2; 1];
|
||||
|
||||
let some_master_key = vec![3;1];
|
||||
let default_master_key = vec![4;1];
|
||||
let some_master_key = vec![3; 1];
|
||||
let default_master_key = vec![4; 1];
|
||||
|
||||
let testdir = create_testdir("key_source")?;
|
||||
|
||||
@ -441,14 +445,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
};
|
||||
|
||||
replace_file(&keypath, &some_key, CreateOptions::default(), false)?;
|
||||
replace_file(&master_keypath, &some_master_key, CreateOptions::default(), false)?;
|
||||
replace_file(
|
||||
&master_keypath,
|
||||
&some_master_key,
|
||||
CreateOptions::default(),
|
||||
false,
|
||||
)?;
|
||||
|
||||
// no params, no default key == no key
|
||||
let res = crypto_parameters(&json!({}));
|
||||
assert_eq!(res.unwrap(), no_key_res);
|
||||
|
||||
// keyfile param == key from keyfile
|
||||
let res = crypto_parameters(&json!({"keyfile": keypath}));
|
||||
let res = crypto_parameters(&json!({ "keyfile": keypath }));
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// crypt mode none == no key
|
||||
@ -469,13 +478,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// invalid keyfile parameter always errors
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
|
||||
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
|
||||
);
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
|
||||
);
|
||||
|
||||
// now set a default key
|
||||
unsafe { set_test_encryption_key(Ok(Some(default_key))); }
|
||||
unsafe {
|
||||
set_test_encryption_key(Ok(Some(default_key)));
|
||||
}
|
||||
|
||||
// and repeat
|
||||
|
||||
@ -484,7 +499,7 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
assert_eq!(res.unwrap(), default_key_res);
|
||||
|
||||
// keyfile param == key from keyfile
|
||||
let res = crypto_parameters(&json!({"keyfile": keypath}));
|
||||
let res = crypto_parameters(&json!({ "keyfile": keypath }));
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// crypt mode none == no key
|
||||
@ -507,13 +522,19 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// invalid keyfile parameter always errors
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
|
||||
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
|
||||
);
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
|
||||
);
|
||||
|
||||
// now make default key retrieval error
|
||||
unsafe { set_test_encryption_key(Err(format_err!("test error"))); }
|
||||
unsafe {
|
||||
set_test_encryption_key(Err(format_err!("test error")));
|
||||
}
|
||||
|
||||
// and repeat
|
||||
|
||||
@ -521,7 +542,7 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
assert!(crypto_parameters(&json!({})).is_err());
|
||||
|
||||
// keyfile param == key from keyfile
|
||||
let res = crypto_parameters(&json!({"keyfile": keypath}));
|
||||
let res = crypto_parameters(&json!({ "keyfile": keypath }));
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// crypt mode none == no key
|
||||
@ -542,18 +563,26 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
assert_eq!(res.unwrap(), some_key_res);
|
||||
|
||||
// invalid keyfile parameter always errors
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err());
|
||||
assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err());
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()
|
||||
);
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()
|
||||
);
|
||||
|
||||
// now remove default key again
|
||||
unsafe { set_test_encryption_key(Ok(None)); }
|
||||
unsafe {
|
||||
set_test_encryption_key(Ok(None));
|
||||
}
|
||||
// set a default master key
|
||||
unsafe { set_test_default_master_pubkey(Ok(Some(default_master_key))); }
|
||||
unsafe {
|
||||
set_test_default_master_pubkey(Ok(Some(default_master_key)));
|
||||
}
|
||||
|
||||
// and use an explicit master key
|
||||
assert!(crypto_parameters(&json!({"master-pubkey-file": master_keypath})).is_err());
|
||||
assert!(crypto_parameters(&json!({ "master-pubkey-file": master_keypath })).is_err());
|
||||
// just a default == no key
|
||||
let res = crypto_parameters(&json!({}));
|
||||
assert_eq!(res.unwrap(), no_key_res);
|
||||
@ -562,35 +591,55 @@ fn test_crypto_parameters_handling() -> Result<(), Error> {
|
||||
let res = crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": master_keypath}));
|
||||
assert_eq!(res.unwrap(), some_key_some_master_res);
|
||||
// same with fallback to default master key
|
||||
let res = crypto_parameters(&json!({"keyfile": keypath}));
|
||||
let res = crypto_parameters(&json!({ "keyfile": keypath }));
|
||||
assert_eq!(res.unwrap(), some_key_default_master_res);
|
||||
|
||||
// crypt mode none == error
|
||||
assert!(crypto_parameters(&json!({"crypt-mode": "none", "master-pubkey-file": master_keypath})).is_err());
|
||||
assert!(crypto_parameters(
|
||||
&json!({"crypt-mode": "none", "master-pubkey-file": master_keypath})
|
||||
)
|
||||
.is_err());
|
||||
// with just default master key == no key
|
||||
let res = crypto_parameters(&json!({"crypt-mode": "none"}));
|
||||
assert_eq!(res.unwrap(), no_key_res);
|
||||
|
||||
// crypt mode encrypt without enc key == error
|
||||
assert!(crypto_parameters(&json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath})).is_err());
|
||||
assert!(crypto_parameters(
|
||||
&json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath})
|
||||
)
|
||||
.is_err());
|
||||
assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err());
|
||||
|
||||
// crypt mode none with explicit key == Error
|
||||
assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath})).is_err());
|
||||
assert!(crypto_parameters(
|
||||
&json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath})
|
||||
)
|
||||
.is_err());
|
||||
assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err());
|
||||
|
||||
// crypt mode encrypt with keyfile == key from keyfile with correct mode
|
||||
let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath}));
|
||||
let res = crypto_parameters(
|
||||
&json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath}),
|
||||
);
|
||||
assert_eq!(res.unwrap(), some_key_some_master_res);
|
||||
let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath}));
|
||||
assert_eq!(res.unwrap(), some_key_default_master_res);
|
||||
|
||||
// invalid master keyfile parameter always errors when a key is passed, even with a valid
|
||||
// default master key
|
||||
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"})).is_err());
|
||||
assert!(
|
||||
crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath}))
|
||||
.is_err()
|
||||
);
|
||||
assert!(crypto_parameters(
|
||||
&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"})
|
||||
)
|
||||
.is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "sign-only"})).is_err());
|
||||
assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"})).is_err());
|
||||
assert!(crypto_parameters(
|
||||
&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"})
|
||||
)
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
//! Shared tools useful for common CLI clients.
|
||||
use std::collections::HashMap;
|
||||
use std::env::VarError::{NotPresent, NotUnicode};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use std::env::VarError::{NotUnicode, NotPresent};
|
||||
use std::io::{BufReader, BufRead};
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::{bail, format_err, Context, Error};
|
||||
use serde_json::{json, Value};
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
use proxmox_schema::*;
|
||||
use proxmox_router::cli::{complete_file_name, shellword_split};
|
||||
use proxmox_schema::*;
|
||||
use proxmox_sys::fs::file_get_json;
|
||||
|
||||
use pbs_api_types::{BACKUP_REPO_URL, Authid, RateLimitConfig, UserWithTokens};
|
||||
use pbs_api_types::{Authid, RateLimitConfig, UserWithTokens, BACKUP_REPO_URL};
|
||||
use pbs_datastore::BackupDir;
|
||||
use pbs_tools::json::json_object_to_query;
|
||||
|
||||
@ -48,7 +48,6 @@ pub const CHUNK_SIZE_SCHEMA: Schema = IntegerSchema::new("Chunk size in KB. Must
|
||||
///
|
||||
/// Only return the first line of data (without CRLF).
|
||||
pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
|
||||
|
||||
let firstline = |data: String| -> String {
|
||||
match data.lines().next() {
|
||||
Some(line) => line.to_string(),
|
||||
@ -68,19 +67,24 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
|
||||
match std::env::var(base_name) {
|
||||
Ok(p) => return Ok(Some(firstline(p))),
|
||||
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", base_name)),
|
||||
Err(NotPresent) => {},
|
||||
Err(NotPresent) => {}
|
||||
};
|
||||
|
||||
let env_name = format!("{}_FD", base_name);
|
||||
match std::env::var(&env_name) {
|
||||
Ok(fd_str) => {
|
||||
let fd: i32 = fd_str.parse()
|
||||
.map_err(|err| format_err!("unable to parse file descriptor in ENV({}): {}", env_name, err))?;
|
||||
let fd: i32 = fd_str.parse().map_err(|err| {
|
||||
format_err!(
|
||||
"unable to parse file descriptor in ENV({}): {}",
|
||||
env_name,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
let mut file = unsafe { File::from_raw_fd(fd) };
|
||||
return Ok(Some(firstline_file(&mut file)?));
|
||||
}
|
||||
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
|
||||
Err(NotPresent) => {},
|
||||
Err(NotPresent) => {}
|
||||
}
|
||||
|
||||
let env_name = format!("{}_FILE", base_name);
|
||||
@ -91,7 +95,7 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
|
||||
return Ok(Some(firstline_file(&mut file)?));
|
||||
}
|
||||
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
|
||||
Err(NotPresent) => {},
|
||||
Err(NotPresent) => {}
|
||||
}
|
||||
|
||||
let env_name = format!("{}_CMD", base_name);
|
||||
@ -104,7 +108,7 @@ pub fn get_secret_from_env(base_name: &str) -> Result<Option<String>, Error> {
|
||||
return Ok(Some(firstline(output)));
|
||||
}
|
||||
Err(NotUnicode(_)) => bail!(format!("{} contains bad characters", env_name)),
|
||||
Err(NotPresent) => {},
|
||||
Err(NotPresent) => {}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
@ -157,21 +161,18 @@ fn connect_do(
|
||||
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
|
||||
|
||||
let password = get_secret_from_env(ENV_VAR_PBS_PASSWORD)?;
|
||||
let options = HttpClientOptions::new_interactive(password, fingerprint)
|
||||
.rate_limit(rate_limit);
|
||||
let options = HttpClientOptions::new_interactive(password, fingerprint).rate_limit(rate_limit);
|
||||
|
||||
HttpClient::new(server, port, auth_id, options)
|
||||
}
|
||||
|
||||
/// like get, but simply ignore errors and return Null instead
|
||||
pub async fn try_get(repo: &BackupRepository, url: &str) -> Value {
|
||||
|
||||
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
|
||||
let password = get_secret_from_env(ENV_VAR_PBS_PASSWORD).unwrap_or(None);
|
||||
|
||||
// ticket cache, but no questions asked
|
||||
let options = HttpClientOptions::new_interactive(password, fingerprint)
|
||||
.interactive(false);
|
||||
let options = HttpClientOptions::new_interactive(password, fingerprint).interactive(false);
|
||||
|
||||
let client = match HttpClient::new(repo.host(), repo.port(), repo.auth_id(), options) {
|
||||
Ok(v) => v,
|
||||
@ -196,7 +197,6 @@ pub fn complete_backup_group(_arg: &str, param: &HashMap<String, String>) -> Vec
|
||||
}
|
||||
|
||||
pub async fn complete_backup_group_do(param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let repo = match extract_repository_from_map(param) {
|
||||
@ -225,8 +225,10 @@ pub fn complete_group_or_snapshot(arg: &str, param: &HashMap<String, String>) ->
|
||||
proxmox_async::runtime::main(async { complete_group_or_snapshot_do(arg, param).await })
|
||||
}
|
||||
|
||||
pub async fn complete_group_or_snapshot_do(arg: &str, param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
pub async fn complete_group_or_snapshot_do(
|
||||
arg: &str,
|
||||
param: &HashMap<String, String>,
|
||||
) -> Vec<String> {
|
||||
if arg.matches('/').count() < 2 {
|
||||
let groups = complete_backup_group_do(param).await;
|
||||
let mut result = vec![];
|
||||
@ -245,7 +247,6 @@ pub fn complete_backup_snapshot(_arg: &str, param: &HashMap<String, String>) ->
|
||||
}
|
||||
|
||||
pub async fn complete_backup_snapshot_do(param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let repo = match extract_repository_from_map(param) {
|
||||
@ -259,9 +260,11 @@ pub async fn complete_backup_snapshot_do(param: &HashMap<String, String>) -> Vec
|
||||
|
||||
if let Some(list) = data.as_array() {
|
||||
for item in list {
|
||||
if let (Some(backup_id), Some(backup_type), Some(backup_time)) =
|
||||
(item["backup-id"].as_str(), item["backup-type"].as_str(), item["backup-time"].as_i64())
|
||||
{
|
||||
if let (Some(backup_id), Some(backup_type), Some(backup_time)) = (
|
||||
item["backup-id"].as_str(),
|
||||
item["backup-type"].as_str(),
|
||||
item["backup-time"].as_i64(),
|
||||
) {
|
||||
if let Ok(snapshot) = BackupDir::new(backup_type, backup_id, backup_time) {
|
||||
result.push(snapshot.relative_path().to_str().unwrap().to_owned());
|
||||
}
|
||||
@ -277,7 +280,6 @@ pub fn complete_server_file_name(_arg: &str, param: &HashMap<String, String>) ->
|
||||
}
|
||||
|
||||
pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let repo = match extract_repository_from_map(param) {
|
||||
@ -286,12 +288,10 @@ pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Ve
|
||||
};
|
||||
|
||||
let snapshot: BackupDir = match param.get("snapshot") {
|
||||
Some(path) => {
|
||||
match path.parse() {
|
||||
Ok(v) => v,
|
||||
_ => return result,
|
||||
}
|
||||
}
|
||||
Some(path) => match path.parse() {
|
||||
Ok(v) => v,
|
||||
_ => return result,
|
||||
},
|
||||
_ => return result,
|
||||
};
|
||||
|
||||
@ -299,7 +299,8 @@ pub async fn complete_server_file_name_do(param: &HashMap<String, String>) -> Ve
|
||||
"backup-type": snapshot.group().backup_type(),
|
||||
"backup-id": snapshot.group().backup_id(),
|
||||
"backup-time": snapshot.backup_time(),
|
||||
})).unwrap();
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let path = format!("api2/json/admin/datastore/{}/files?{}", repo.store(), query);
|
||||
|
||||
@ -350,14 +351,15 @@ pub fn complete_img_archive_name(arg: &str, param: &HashMap<String, String>) ->
|
||||
}
|
||||
|
||||
pub fn complete_chunk_size(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let mut size = 64;
|
||||
loop {
|
||||
result.push(size.to_string());
|
||||
size *= 2;
|
||||
if size > 4096 { break; }
|
||||
if size > 4096 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
@ -368,7 +370,6 @@ pub fn complete_auth_id(_arg: &str, param: &HashMap<String, String>) -> Vec<Stri
|
||||
}
|
||||
|
||||
pub async fn complete_auth_id_do(param: &HashMap<String, String>) -> Vec<String> {
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let repo = match extract_repository_from_map(param) {
|
||||
|
Reference in New Issue
Block a user