client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
use std::collections::HashMap;
|
2020-11-30 09:48:34 +00:00
|
|
|
use std::ffi::OsStr;
|
2020-10-07 11:53:05 +00:00
|
|
|
use std::hash::BuildHasher;
|
2020-11-30 09:48:34 +00:00
|
|
|
use std::os::unix::io::AsRawFd;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::sync::Arc;
|
2020-07-02 15:49:08 +00:00
|
|
|
|
|
|
|
use anyhow::{bail, format_err, Error};
|
|
|
|
use futures::future::FutureExt;
|
2020-11-30 09:48:34 +00:00
|
|
|
use futures::select;
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
use futures::stream::{StreamExt, TryStreamExt};
|
2020-11-30 09:48:34 +00:00
|
|
|
use nix::unistd::{fork, ForkResult};
|
|
|
|
use serde_json::Value;
|
|
|
|
use tokio::signal::unix::{signal, SignalKind};
|
2020-07-02 15:49:08 +00:00
|
|
|
|
|
|
|
use proxmox::{sortable, identity};
|
2020-07-02 16:04:29 +00:00
|
|
|
use proxmox::api::{ApiHandler, ApiMethod, RpcEnvironment, schema::*, cli::*};
|
2020-11-30 09:48:34 +00:00
|
|
|
use proxmox::tools::fd::Fd;
|
2020-07-02 15:49:08 +00:00
|
|
|
|
|
|
|
use proxmox_backup::tools;
|
|
|
|
use proxmox_backup::backup::{
|
|
|
|
load_and_decrypt_key,
|
|
|
|
CryptConfig,
|
|
|
|
IndexFile,
|
|
|
|
BackupDir,
|
|
|
|
BackupGroup,
|
|
|
|
BufferedDynamicReader,
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
AsyncIndexReader,
|
2020-07-02 15:49:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use proxmox_backup::client::*;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
REPO_URL_SCHEMA,
|
|
|
|
extract_repository_from_value,
|
|
|
|
complete_pxar_archive_name,
|
2020-10-06 07:17:58 +00:00
|
|
|
complete_img_archive_name,
|
2020-07-02 15:49:08 +00:00
|
|
|
complete_group_or_snapshot,
|
|
|
|
complete_repository,
|
|
|
|
record_repository,
|
|
|
|
connect,
|
|
|
|
api_datastore_latest_snapshot,
|
|
|
|
BufferedDynamicReadAt,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[sortable]
|
|
|
|
const API_METHOD_MOUNT: ApiMethod = ApiMethod::new(
|
|
|
|
&ApiHandler::Sync(&mount),
|
|
|
|
&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()),
|
|
|
|
("repository", true, &REPO_URL_SCHEMA),
|
|
|
|
("keyfile", true, &StringSchema::new("Path to encryption key.").schema()),
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
("verbose", true, &BooleanSchema::new("Verbose output and stay in foreground.").default(false).schema()),
|
|
|
|
]),
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
#[sortable]
|
|
|
|
const API_METHOD_MAP: ApiMethod = ApiMethod::new(
|
|
|
|
&ApiHandler::Sync(&mount),
|
|
|
|
&ObjectSchema::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()),
|
|
|
|
("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()),
|
|
|
|
]),
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
#[sortable]
|
|
|
|
const API_METHOD_UNMAP: ApiMethod = ApiMethod::new(
|
|
|
|
&ApiHandler::Sync(&unmap),
|
|
|
|
&ObjectSchema::new(
|
|
|
|
"Unmap a loop device mapped with 'map' and release all resources.",
|
|
|
|
&sorted!([
|
2020-10-07 11:53:05 +00:00
|
|
|
("name", true, &StringSchema::new(
|
2020-10-07 11:53:06 +00:00
|
|
|
concat!("Archive name, path to loopdev (/dev/loopX) or loop device number. ",
|
|
|
|
"Omit to list all current mappings and force cleaning up leftover instances.")
|
2020-10-07 11:53:05 +00:00
|
|
|
).schema()),
|
2020-07-02 15:49:08 +00:00
|
|
|
]),
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
pub fn mount_cmd_def() -> CliCommand {
|
|
|
|
|
|
|
|
CliCommand::new(&API_METHOD_MOUNT)
|
|
|
|
.arg_param(&["snapshot", "archive-name", "target"])
|
|
|
|
.completion_cb("repository", complete_repository)
|
|
|
|
.completion_cb("snapshot", complete_group_or_snapshot)
|
|
|
|
.completion_cb("archive-name", complete_pxar_archive_name)
|
|
|
|
.completion_cb("target", tools::complete_file_name)
|
|
|
|
}
|
|
|
|
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
pub fn map_cmd_def() -> CliCommand {
|
|
|
|
|
|
|
|
CliCommand::new(&API_METHOD_MAP)
|
|
|
|
.arg_param(&["snapshot", "archive-name"])
|
|
|
|
.completion_cb("repository", complete_repository)
|
|
|
|
.completion_cb("snapshot", complete_group_or_snapshot)
|
2020-10-06 07:17:58 +00:00
|
|
|
.completion_cb("archive-name", complete_img_archive_name)
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unmap_cmd_def() -> CliCommand {
|
|
|
|
|
|
|
|
CliCommand::new(&API_METHOD_UNMAP)
|
2020-10-07 11:53:05 +00:00
|
|
|
.arg_param(&["name"])
|
|
|
|
.completion_cb("name", complete_mapping_names)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn complete_mapping_names<S: BuildHasher>(_arg: &str, _param: &HashMap<String, String, S>)
|
|
|
|
-> Vec<String>
|
|
|
|
{
|
|
|
|
match tools::fuse_loop::find_all_mappings() {
|
|
|
|
Ok(mappings) => mappings
|
|
|
|
.filter_map(|(name, _)| {
|
|
|
|
tools::systemd::unescape_unit(&name).ok()
|
|
|
|
}).collect(),
|
|
|
|
Err(_) => Vec::new()
|
|
|
|
}
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-02 15:49:08 +00:00
|
|
|
fn mount(
|
|
|
|
param: Value,
|
|
|
|
_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
|
|
|
|
// passed for the RawFd.
|
|
|
|
return proxmox_backup::tools::runtime::main(mount_do(param, None));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process should be deamonized.
|
|
|
|
// Make sure to fork before the async runtime is instantiated to avoid troubles.
|
2020-11-30 09:48:34 +00:00
|
|
|
let (pr, pw) = proxmox_backup::tools::pipe()?;
|
2020-10-19 10:12:33 +00:00
|
|
|
match unsafe { fork() } {
|
2020-07-02 15:49:08 +00:00
|
|
|
Ok(ForkResult::Parent { .. }) => {
|
2020-11-30 09:48:34 +00:00
|
|
|
drop(pw);
|
2020-07-02 15:49:08 +00:00
|
|
|
// Blocks the parent process until we are ready to go in the child
|
2020-11-30 09:48:34 +00:00
|
|
|
let _res = nix::unistd::read(pr.as_raw_fd(), &mut [0]).unwrap();
|
2020-07-02 15:49:08 +00:00
|
|
|
Ok(Value::Null)
|
|
|
|
}
|
|
|
|
Ok(ForkResult::Child) => {
|
2020-11-30 09:48:34 +00:00
|
|
|
drop(pr);
|
2020-07-02 15:49:08 +00:00
|
|
|
nix::unistd::setsid().unwrap();
|
2020-11-30 09:48:34 +00:00
|
|
|
proxmox_backup::tools::runtime::main(mount_do(param, Some(pw)))
|
2020-07-02 15:49:08 +00:00
|
|
|
}
|
|
|
|
Err(_) => bail!("failed to daemonize process"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:48:34 +00:00
|
|
|
async fn mount_do(param: Value, pipe: Option<Fd>) -> Result<Value, Error> {
|
2020-07-02 15:49:08 +00:00
|
|
|
let repo = extract_repository_from_value(¶m)?;
|
|
|
|
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
2020-11-10 10:54:50 +00:00
|
|
|
let client = connect(&repo)?;
|
2020-07-02 15:49:08 +00:00
|
|
|
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
let target = param["target"].as_str();
|
|
|
|
|
2020-07-02 15:49:08 +00:00
|
|
|
record_repository(&repo);
|
|
|
|
|
|
|
|
let path = tools::required_string_param(¶m, "snapshot")?;
|
|
|
|
let (backup_type, backup_id, backup_time) = if path.matches('/').count() == 1 {
|
|
|
|
let group: BackupGroup = path.parse()?;
|
|
|
|
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())
|
|
|
|
};
|
|
|
|
|
|
|
|
let keyfile = param["keyfile"].as_str().map(PathBuf::from);
|
|
|
|
let crypt_config = match keyfile {
|
|
|
|
None => None,
|
|
|
|
Some(path) => {
|
2020-11-20 16:38:33 +00:00
|
|
|
println!("Encryption key file: '{:?}'", path);
|
|
|
|
let (key, _, fingerprint) = load_and_decrypt_key(&path, &crate::key::get_encryption_key_password)?;
|
|
|
|
println!("Encryption key fingerprint: '{}'", fingerprint);
|
2020-07-02 15:49:08 +00:00
|
|
|
Some(Arc::new(CryptConfig::new(key)?))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let server_archive_name = if archive_name.ends_with(".pxar") {
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
if let None = target {
|
|
|
|
bail!("use the 'mount' command to mount pxar archives");
|
|
|
|
}
|
2020-07-02 15:49:08 +00:00
|
|
|
format!("{}.didx", archive_name)
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
} else if archive_name.ends_with(".img") {
|
|
|
|
if let Some(_) = target {
|
|
|
|
bail!("use the 'map' command to map drive images");
|
|
|
|
}
|
|
|
|
format!("{}.fidx", archive_name)
|
2020-07-02 15:49:08 +00:00
|
|
|
} else {
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
bail!("Can only mount/map pxar archives and drive images.");
|
2020-07-02 15:49:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let client = BackupReader::start(
|
|
|
|
client,
|
|
|
|
crypt_config.clone(),
|
|
|
|
repo.store(),
|
|
|
|
&backup_type,
|
|
|
|
&backup_id,
|
|
|
|
backup_time,
|
|
|
|
true,
|
|
|
|
).await?;
|
|
|
|
|
2020-07-08 14:07:14 +00:00
|
|
|
let (manifest, _) = client.download_manifest().await?;
|
2020-11-20 16:38:37 +00:00
|
|
|
manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
|
2020-07-02 15:49:08 +00:00
|
|
|
|
2020-09-08 13:33:54 +00:00
|
|
|
let file_info = manifest.lookup_file_info(&server_archive_name)?;
|
2020-08-10 11:25:07 +00:00
|
|
|
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
let daemonize = || -> Result<(), Error> {
|
2020-07-02 15:49:08 +00:00
|
|
|
if let Some(pipe) = pipe {
|
|
|
|
nix::unistd::chdir(Path::new("/")).unwrap();
|
|
|
|
// Finish creation of daemon by redirecting filedescriptors.
|
|
|
|
let nullfd = nix::fcntl::open(
|
|
|
|
"/dev/null",
|
|
|
|
nix::fcntl::OFlag::O_RDWR,
|
|
|
|
nix::sys::stat::Mode::empty(),
|
|
|
|
).unwrap();
|
|
|
|
nix::unistd::dup2(nullfd, 0).unwrap();
|
|
|
|
nix::unistd::dup2(nullfd, 1).unwrap();
|
|
|
|
nix::unistd::dup2(nullfd, 2).unwrap();
|
|
|
|
if nullfd > 2 {
|
|
|
|
nix::unistd::close(nullfd).unwrap();
|
|
|
|
}
|
|
|
|
// Signal the parent process that we are done with the setup and it can
|
|
|
|
// terminate.
|
2020-11-30 09:48:34 +00:00
|
|
|
nix::unistd::write(pipe.as_raw_fd(), &[0u8])?;
|
|
|
|
let _: Fd = pipe;
|
2020-07-02 15:49:08 +00:00
|
|
|
}
|
|
|
|
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
let options = OsStr::new("ro,default_permissions");
|
|
|
|
|
|
|
|
// handle SIGINT and SIGTERM
|
|
|
|
let mut interrupt_int = signal(SignalKind::interrupt())?;
|
|
|
|
let mut interrupt_term = signal(SignalKind::terminate())?;
|
2021-01-11 08:52:52 +00:00
|
|
|
|
|
|
|
let mut interrupt = futures::future::select(interrupt_int.recv().boxed(), interrupt_term.recv().boxed());
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
|
|
|
|
if server_archive_name.ends_with(".didx") {
|
|
|
|
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 reader = BufferedDynamicReader::new(index, chunk_reader);
|
|
|
|
let archive_size = reader.archive_size();
|
|
|
|
let reader: proxmox_backup::pxar::fuse::Reader =
|
|
|
|
Arc::new(BufferedDynamicReadAt::new(reader));
|
|
|
|
let decoder = proxmox_backup::pxar::fuse::Accessor::new(reader, archive_size).await?;
|
|
|
|
|
|
|
|
let session = proxmox_backup::pxar::fuse::Session::mount(
|
|
|
|
decoder,
|
|
|
|
&options,
|
|
|
|
false,
|
|
|
|
Path::new(target.unwrap()),
|
|
|
|
)
|
|
|
|
.map_err(|err| format_err!("pxar mount failed: {}", err))?;
|
|
|
|
|
|
|
|
daemonize()?;
|
2020-10-05 08:57:57 +00:00
|
|
|
|
2020-07-02 15:49:08 +00:00
|
|
|
select! {
|
|
|
|
res = session.fuse() => res?,
|
2020-10-05 08:57:57 +00:00
|
|
|
_ = interrupt => {
|
2020-07-02 15:49:08 +00:00
|
|
|
// exit on interrupted
|
|
|
|
}
|
|
|
|
}
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
} else if server_archive_name.ends_with(".fidx") {
|
|
|
|
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 reader = AsyncIndexReader::new(index, chunk_reader);
|
|
|
|
|
2020-10-07 11:53:05 +00:00
|
|
|
let name = &format!("{}:{}/{}", repo.to_string(), path, archive_name);
|
|
|
|
let name_escaped = tools::systemd::escape_unit(name, false);
|
|
|
|
|
|
|
|
let mut session = tools::fuse_loop::FuseLoopSession::map_loop(size, reader, &name_escaped, options).await?;
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
let loopdev = session.loopdev_path.clone();
|
|
|
|
|
|
|
|
let (st_send, st_recv) = futures::channel::mpsc::channel(1);
|
|
|
|
let (mut abort_send, abort_recv) = futures::channel::mpsc::channel(1);
|
|
|
|
let mut st_recv = st_recv.fuse();
|
|
|
|
let mut session_fut = session.main(st_send, abort_recv).boxed().fuse();
|
|
|
|
|
|
|
|
// poll until loop file is mapped (or errors)
|
|
|
|
select! {
|
|
|
|
res = session_fut => {
|
|
|
|
bail!("FUSE session unexpectedly ended before loop file mapping");
|
|
|
|
},
|
|
|
|
res = st_recv.try_next() => {
|
|
|
|
if let Err(err) = res {
|
|
|
|
// init went wrong, abort now
|
|
|
|
abort_send.try_send(()).map_err(|err|
|
|
|
|
format_err!("error while sending abort signal - {}", err))?;
|
|
|
|
// ignore and keep original error cause
|
|
|
|
let _ = session_fut.await;
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// daemonize only now to be able to print mapped loopdev or startup errors
|
2020-10-07 11:53:05 +00:00
|
|
|
println!("Image '{}' mapped on {}", name, loopdev);
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
daemonize()?;
|
|
|
|
|
|
|
|
// continue polling until complete or interrupted (which also happens on unmap)
|
|
|
|
select! {
|
|
|
|
res = session_fut => res?,
|
|
|
|
_ = interrupt => {
|
|
|
|
// exit on interrupted
|
|
|
|
abort_send.try_send(()).map_err(|err|
|
|
|
|
format_err!("error while sending abort signal - {}", err))?;
|
|
|
|
session_fut.await?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("Image unmapped");
|
2020-07-02 15:49:08 +00:00
|
|
|
} else {
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
bail!("unknown archive file extension (expected .pxar or .img)");
|
2020-07-02 15:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Value::Null)
|
|
|
|
}
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
|
|
|
|
fn unmap(
|
|
|
|
param: Value,
|
|
|
|
_info: &ApiMethod,
|
|
|
|
_rpcenv: &mut dyn RpcEnvironment,
|
|
|
|
) -> Result<Value, Error> {
|
|
|
|
|
2020-10-07 11:53:05 +00:00
|
|
|
let mut name = match param["name"].as_str() {
|
|
|
|
Some(name) => name.to_owned(),
|
|
|
|
None => {
|
2020-10-07 11:53:06 +00:00
|
|
|
tools::fuse_loop::cleanup_unused_run_files(None);
|
2020-10-07 11:53:05 +00:00
|
|
|
let mut any = false;
|
|
|
|
for (backing, loopdev) in tools::fuse_loop::find_all_mappings()? {
|
|
|
|
let name = tools::systemd::unescape_unit(&backing)?;
|
|
|
|
println!("{}:\t{}", loopdev.unwrap_or("(unmapped)".to_owned()), name);
|
|
|
|
any = true;
|
|
|
|
}
|
|
|
|
if !any {
|
|
|
|
println!("Nothing mapped.");
|
|
|
|
}
|
|
|
|
return Ok(Value::Null);
|
|
|
|
},
|
|
|
|
};
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
|
2020-10-07 11:53:05 +00:00
|
|
|
// allow loop device number alone
|
|
|
|
if let Ok(num) = name.parse::<u8>() {
|
|
|
|
name = format!("/dev/loop{}", num);
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-07 11:53:05 +00:00
|
|
|
if name.starts_with("/dev/loop") {
|
|
|
|
tools::fuse_loop::unmap_loopdev(name)?;
|
|
|
|
} else {
|
|
|
|
let name = tools::systemd::escape_unit(&name, false);
|
|
|
|
tools::fuse_loop::unmap_name(name)?;
|
|
|
|
}
|
client: implement map/unmap commands for .img backups
Allows mapping fixed-index .img files (usually from VM backups) to be
mapped to a local loopback device.
The architecture uses a FUSE-backed temp file mapped to a loopdev:
/dev/loopX -> FUSE /run/pbs-loopdev/xxx -> backup client -> PBS
Since unmapping requires some cleanup (unmap the loopdev, stop FUSE,
remove the temp files) a special 'unmap' command is added, which uses a
PID file to send SIGINT to the backup-client instance started with
'map', which will handle the cleanup itself.
The polling with select! in mount.rs needs to be split in two, since we
have a chicken and egg problem between running FUSE and setting up the
loop device - so we need to do them concurrently, until the loopdev is
assigned, at which point we can report success and daemonize, and then
continue polling the FUSE loop future.
A loopdev module is added to tools containing all required functions for
mapping a loop device to the FUSE file, with the ioctls moved into an
inline module to avoid exposing them directly.
The client code is placed in the 'mount' module, which, while
admittedly a loose fit, allows reuse of the daemonizing code.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-10-05 08:57:58 +00:00
|
|
|
|
|
|
|
Ok(Value::Null)
|
|
|
|
}
|