mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
@@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize};
|
||||
use tracing::instrument;
|
||||
|
||||
use self::util::DiskInfo;
|
||||
use crate::context::RpcContext;
|
||||
use crate::disk::util::{BackupMountGuard, TmpMountGuard};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::{display_serializable, IoFormat, Version};
|
||||
@@ -17,7 +16,7 @@ use crate::Error;
|
||||
pub mod main;
|
||||
pub mod util;
|
||||
|
||||
#[command(subcommands(list))]
|
||||
#[command(subcommands(list, backup_info))]
|
||||
pub fn disk() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -139,14 +138,13 @@ fn display_backup_info(info: BackupInfo, matches: &ArgMatches<'_>) {
|
||||
}
|
||||
|
||||
#[command(rename = "backup-info", display(display_backup_info))]
|
||||
#[instrument(skip(ctx, password))]
|
||||
#[instrument(skip(password))]
|
||||
pub async fn backup_info(
|
||||
#[context] ctx: RpcContext,
|
||||
#[arg] logicalname: PathBuf,
|
||||
#[arg] password: String,
|
||||
) -> Result<BackupInfo, Error> {
|
||||
let guard =
|
||||
BackupMountGuard::mount(TmpMountGuard::mount(logicalname).await?, &password).await?;
|
||||
BackupMountGuard::mount(TmpMountGuard::mount(logicalname, None).await?, &password).await?;
|
||||
|
||||
let res = guard.metadata.clone();
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use color_eyre::eyre::{self, eyre};
|
||||
use digest::Digest;
|
||||
use futures::TryStreamExt;
|
||||
use indexmap::IndexSet;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::BackupInfo;
|
||||
@@ -18,7 +21,7 @@ use crate::auth::check_password;
|
||||
use crate::middleware::encrypt::{decrypt_slice, encrypt_slice};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::io::from_yaml_async_reader;
|
||||
use crate::util::{AtomicFile, FileLock, GeneralGuard, Invoke, IoFormat, Version};
|
||||
use crate::util::{AtomicFile, FileLock, Invoke, IoFormat, Version};
|
||||
use crate::volume::BACKUP_DIR;
|
||||
use crate::{Error, ResultExt as _};
|
||||
|
||||
@@ -158,7 +161,10 @@ pub async fn list() -> Result<Vec<DiskInfo>, Error> {
|
||||
.filter_map(|c| c.get(1))
|
||||
.map(|d| Path::new("/dev").join(d.as_str()))
|
||||
.collect(),
|
||||
Err(e) => BTreeSet::new(),
|
||||
Err(e) => {
|
||||
tracing::warn!("`zpool status` returned error: {}", e);
|
||||
BTreeSet::new()
|
||||
}
|
||||
};
|
||||
let disks = tokio_stream::wrappers::ReadDirStream::new(
|
||||
tokio::fs::read_dir(DISK_PATH)
|
||||
@@ -241,71 +247,67 @@ pub async fn list() -> Result<Vec<DiskInfo>, Error> {
|
||||
.unwrap_or_default();
|
||||
let mut used = None;
|
||||
|
||||
let tmp_mountpoint =
|
||||
Path::new(TMP_MOUNTPOINT).join(&part.strip_prefix("/").unwrap_or(&part));
|
||||
if let Err(e) = mount(&part, &tmp_mountpoint).await {
|
||||
tracing::warn!("Could not collect usage information: {}", e.source)
|
||||
} else {
|
||||
let mount_guard = GeneralGuard::new(|| {
|
||||
let path = tmp_mountpoint.clone();
|
||||
tokio::spawn(unmount(path))
|
||||
});
|
||||
used = get_used(&tmp_mountpoint)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::warn!(
|
||||
"Could not get usage of {}: {}",
|
||||
part.display(),
|
||||
e.source
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
let backup_unencrypted_metadata_path =
|
||||
tmp_mountpoint.join("EmbassyBackups/unencrypted-metadata.cbor");
|
||||
if tokio::fs::metadata(&backup_unencrypted_metadata_path)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
embassy_os = match (|| async {
|
||||
IoFormat::Cbor.from_slice(
|
||||
&tokio::fs::read(&backup_unencrypted_metadata_path)
|
||||
.await
|
||||
.with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
backup_unencrypted_metadata_path.display().to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
})()
|
||||
.await
|
||||
match TmpMountGuard::mount(&part, None).await {
|
||||
Err(e) => tracing::warn!("Could not collect usage information: {}", e.source),
|
||||
Ok(mount_guard) => {
|
||||
used = get_used(&mount_guard)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::warn!(
|
||||
"Could not get usage of {}: {}",
|
||||
part.display(),
|
||||
e.source
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
let backup_unencrypted_metadata_path = mount_guard
|
||||
.as_ref()
|
||||
.join("EmbassyBackups/unencrypted-metadata.cbor");
|
||||
if tokio::fs::metadata(&backup_unencrypted_metadata_path)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
Ok(a) => Some(a),
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Error fetching unencrypted backup metadata: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
} else if label.as_deref() == Some("rootfs") {
|
||||
let version_path =
|
||||
tmp_mountpoint.join("root").join("appmgr").join("version");
|
||||
if tokio::fs::metadata(&version_path).await.is_ok() {
|
||||
embassy_os = Some(EmbassyOsRecoveryInfo {
|
||||
version: from_yaml_async_reader(File::open(&version_path).await?)
|
||||
embassy_os = match (|| async {
|
||||
IoFormat::Cbor.from_slice(
|
||||
&tokio::fs::read(&backup_unencrypted_metadata_path)
|
||||
.await
|
||||
.with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
backup_unencrypted_metadata_path
|
||||
.display()
|
||||
.to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
})()
|
||||
.await
|
||||
{
|
||||
Ok(a) => Some(a),
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Error fetching unencrypted backup metadata: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
} else if label.as_deref() == Some("rootfs") {
|
||||
let version_path = mount_guard.as_ref().join("root/appmgr/version");
|
||||
if tokio::fs::metadata(&version_path).await.is_ok() {
|
||||
embassy_os = Some(EmbassyOsRecoveryInfo {
|
||||
version: from_yaml_async_reader(
|
||||
File::open(&version_path).await?,
|
||||
)
|
||||
.await?,
|
||||
full: true,
|
||||
password_hash: None,
|
||||
wrapped_key: None,
|
||||
});
|
||||
full: true,
|
||||
password_hash: None,
|
||||
wrapped_key: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
mount_guard.unmount().await?;
|
||||
}
|
||||
mount_guard
|
||||
.drop()
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Unknown)??;
|
||||
}
|
||||
|
||||
partitions.push(PartitionInfo {
|
||||
@@ -361,11 +363,11 @@ pub async fn mount(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(src, dst, password))]
|
||||
#[instrument(skip(src, dst, key))]
|
||||
pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
src: P0,
|
||||
dst: P1,
|
||||
password: &str,
|
||||
key: &str,
|
||||
) -> Result<(), Error> {
|
||||
let is_mountpoint = tokio::process::Command::new("mountpoint")
|
||||
.arg(dst.as_ref())
|
||||
@@ -383,7 +385,7 @@ pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
.arg(src.as_ref())
|
||||
.arg(dst.as_ref())
|
||||
.arg("-o")
|
||||
.arg(format!("key=passphrase,passwd={},ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=y", password))
|
||||
.arg(format!("key=passphrase,passwd={},ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=y", key))
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()?;
|
||||
@@ -422,6 +424,7 @@ pub async fn bind<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
if is_mountpoint.success() {
|
||||
unmount(dst.as_ref()).await?;
|
||||
}
|
||||
tokio::fs::create_dir_all(&src).await?;
|
||||
tokio::fs::create_dir_all(&dst).await?;
|
||||
let mut mount_cmd = tokio::process::Command::new("mount");
|
||||
mount_cmd.arg("--bind");
|
||||
@@ -432,17 +435,7 @@ pub async fn bind<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
.arg(src.as_ref())
|
||||
.arg(dst.as_ref())
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::new(
|
||||
e.source.wrap_err(format!(
|
||||
"Binding {} to {}",
|
||||
src.as_ref().display(),
|
||||
dst.as_ref().display(),
|
||||
)),
|
||||
e.kind,
|
||||
)
|
||||
})?;
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -450,6 +443,7 @@ pub async fn bind<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
pub async fn unmount<P: AsRef<Path>>(mountpoint: P) -> Result<(), Error> {
|
||||
tracing::debug!("Unmounting {}.", mountpoint.as_ref().display());
|
||||
let umount_output = tokio::process::Command::new("umount")
|
||||
.arg("-l")
|
||||
.arg(mountpoint.as_ref())
|
||||
.output()
|
||||
.await?;
|
||||
@@ -472,10 +466,11 @@ pub async fn unmount<P: AsRef<Path>>(mountpoint: P) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait GenericMountGuard: AsRef<Path> {
|
||||
pub trait GenericMountGuard: AsRef<Path> + std::fmt::Debug + Send + Sync + 'static {
|
||||
async fn unmount(mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MountGuard {
|
||||
mountpoint: PathBuf,
|
||||
mounted: bool,
|
||||
@@ -484,9 +479,14 @@ impl MountGuard {
|
||||
pub async fn mount(
|
||||
logicalname: impl AsRef<Path>,
|
||||
mountpoint: impl AsRef<Path>,
|
||||
encryption_key: Option<&str>,
|
||||
) -> Result<Self, Error> {
|
||||
let mountpoint = mountpoint.as_ref().to_owned();
|
||||
mount(logicalname, &mountpoint).await?;
|
||||
if let Some(key) = encryption_key {
|
||||
mount_ecryptfs(logicalname, &mountpoint, key).await?;
|
||||
} else {
|
||||
mount(logicalname, &mountpoint).await?;
|
||||
}
|
||||
Ok(MountGuard {
|
||||
mountpoint,
|
||||
mounted: true,
|
||||
@@ -538,27 +538,45 @@ async fn tmp_mountpoint(source: impl AsRef<Path>) -> Result<PathBuf, Error> {
|
||||
)))
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref TMP_MOUNTS: Mutex<BTreeMap<PathBuf, Weak<MountGuard>>> = Mutex::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TmpMountGuard {
|
||||
guard: MountGuard,
|
||||
lock: FileLock,
|
||||
guard: Arc<MountGuard>,
|
||||
}
|
||||
impl TmpMountGuard {
|
||||
pub async fn mount(logicalname: impl AsRef<Path>) -> Result<Self, Error> {
|
||||
#[instrument(skip(logicalname, encryption_key))]
|
||||
pub async fn mount(
|
||||
logicalname: impl AsRef<Path>,
|
||||
encryption_key: Option<&str>,
|
||||
) -> Result<Self, Error> {
|
||||
let mountpoint = tmp_mountpoint(&logicalname).await?;
|
||||
let lock = FileLock::new(mountpoint.with_extension("lock")).await?;
|
||||
let guard = MountGuard::mount(logicalname, &mountpoint).await?;
|
||||
Ok(TmpMountGuard { guard, lock })
|
||||
let mut tmp_mounts = TMP_MOUNTS.lock().await;
|
||||
if !tmp_mounts.contains_key(&mountpoint) {
|
||||
tmp_mounts.insert(mountpoint.clone(), Weak::new());
|
||||
}
|
||||
let weak_slot = tmp_mounts.get_mut(&mountpoint).unwrap();
|
||||
if let Some(guard) = weak_slot.upgrade() {
|
||||
Ok(TmpMountGuard { guard })
|
||||
} else {
|
||||
let guard =
|
||||
Arc::new(MountGuard::mount(logicalname, &mountpoint, encryption_key).await?);
|
||||
*weak_slot = Arc::downgrade(&guard);
|
||||
Ok(TmpMountGuard { guard })
|
||||
}
|
||||
}
|
||||
pub async fn unmount(self) -> Result<(), Error> {
|
||||
let TmpMountGuard { guard, lock } = self;
|
||||
guard.unmount().await?;
|
||||
lock.unlock().await?;
|
||||
if let Ok(guard) = Arc::try_unwrap(self.guard) {
|
||||
guard.unmount().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl AsRef<Path> for TmpMountGuard {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.guard.as_ref()
|
||||
(&*self.guard).as_ref()
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
@@ -570,11 +588,10 @@ impl GenericMountGuard for TmpMountGuard {
|
||||
|
||||
pub struct BackupMountGuard<G: GenericMountGuard> {
|
||||
backup_disk_mount_guard: Option<G>,
|
||||
encrypted_guard: Option<TmpMountGuard>,
|
||||
enc_key: String,
|
||||
pub unencrypted_metadata: EmbassyOsRecoveryInfo,
|
||||
pub metadata: BackupInfo,
|
||||
mountpoint: PathBuf,
|
||||
mounted: bool,
|
||||
}
|
||||
impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
fn backup_disk_path(&self) -> &Path {
|
||||
@@ -585,12 +602,12 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(password))]
|
||||
pub async fn mount(backup_disk_mount_guard: G, password: &str) -> Result<Self, Error> {
|
||||
let mountpoint = tmp_mountpoint(&backup_disk_mount_guard).await?;
|
||||
let backup_disk_path = backup_disk_mount_guard.as_ref();
|
||||
let unencrypted_metadata_path =
|
||||
backup_disk_path.join("EmbassyBackups/unencrypted-metadata.cbor");
|
||||
let unencrypted_metadata: EmbassyOsRecoveryInfo =
|
||||
let mut unencrypted_metadata: EmbassyOsRecoveryInfo =
|
||||
if tokio::fs::metadata(&unencrypted_metadata_path)
|
||||
.await
|
||||
.is_ok()
|
||||
@@ -613,7 +630,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
unencrypted_metadata.wrapped_key.as_ref(),
|
||||
) {
|
||||
let wrapped_key =
|
||||
base32::decode(base32::Alphabet::RFC4648 { padding: false }, wrapped_key)
|
||||
base32::decode(base32::Alphabet::RFC4648 { padding: true }, wrapped_key)
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("failed to decode wrapped key"),
|
||||
@@ -629,6 +646,23 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
)
|
||||
};
|
||||
|
||||
if unencrypted_metadata.password_hash.is_none() {
|
||||
unencrypted_metadata.password_hash = Some(
|
||||
argon2::hash_encoded(
|
||||
password.as_bytes(),
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default(),
|
||||
)
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
||||
);
|
||||
}
|
||||
if unencrypted_metadata.wrapped_key.is_none() {
|
||||
unencrypted_metadata.wrapped_key = Some(base32::encode(
|
||||
base32::Alphabet::RFC4648 { padding: true },
|
||||
&encrypt_slice(&enc_key, password),
|
||||
));
|
||||
}
|
||||
|
||||
let crypt_path = backup_disk_path.join("EmbassyBackups/crypt");
|
||||
if tokio::fs::metadata(&crypt_path).await.is_err() {
|
||||
tokio::fs::create_dir_all(&crypt_path).await.with_ctx(|_| {
|
||||
@@ -638,38 +672,26 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
)
|
||||
})?;
|
||||
}
|
||||
mount_ecryptfs(&crypt_path, &mountpoint, &enc_key).await?;
|
||||
let metadata = match async {
|
||||
let metadata_path = mountpoint.join("metadata.cbor");
|
||||
let metadata: BackupInfo = if tokio::fs::metadata(&metadata_path).await.is_ok() {
|
||||
IoFormat::Cbor.from_slice(&tokio::fs::read(&metadata_path).await.with_ctx(
|
||||
|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
metadata_path.display().to_string(),
|
||||
)
|
||||
},
|
||||
)?)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
Ok(metadata)
|
||||
}
|
||||
.await
|
||||
{
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
unmount(&mountpoint).await?;
|
||||
return Err(e);
|
||||
}
|
||||
let encrypted_guard = TmpMountGuard::mount(&crypt_path, Some(&enc_key)).await?;
|
||||
|
||||
let metadata_path = encrypted_guard.as_ref().join("metadata.cbor");
|
||||
let metadata: BackupInfo = if tokio::fs::metadata(&metadata_path).await.is_ok() {
|
||||
IoFormat::Cbor.from_slice(&tokio::fs::read(&metadata_path).await.with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
metadata_path.display().to_string(),
|
||||
)
|
||||
})?)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
backup_disk_mount_guard: Some(backup_disk_mount_guard),
|
||||
encrypted_guard: Some(encrypted_guard),
|
||||
enc_key,
|
||||
unencrypted_metadata,
|
||||
metadata,
|
||||
mountpoint,
|
||||
mounted: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -689,22 +711,23 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn mount_package_backup(
|
||||
&self,
|
||||
id: &PackageId,
|
||||
) -> Result<PackageBackupMountGuard, Error> {
|
||||
let lock = FileLock::new(Path::new(BACKUP_DIR).join(format!("{}.lock", id))).await?;
|
||||
let lock = FileLock::new(Path::new(BACKUP_DIR).join(format!("{}.lock", id)), false).await?;
|
||||
let mountpoint = Path::new(BACKUP_DIR).join(id);
|
||||
bind(self.mountpoint.join(id), &mountpoint, false).await?;
|
||||
bind(self.as_ref().join(id), &mountpoint, false).await?;
|
||||
Ok(PackageBackupMountGuard {
|
||||
mountpoint,
|
||||
lock,
|
||||
mounted: true,
|
||||
mountpoint: Some(mountpoint),
|
||||
lock: Some(lock),
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn save(&self) -> Result<(), Error> {
|
||||
let metadata_path = self.mountpoint.join("metadata.cbor");
|
||||
let metadata_path = self.as_ref().join("metadata.cbor");
|
||||
let backup_disk_path = self.backup_disk_path();
|
||||
let mut file = AtomicFile::new(&metadata_path).await?;
|
||||
file.write_all(&IoFormat::Cbor.to_vec(&self.metadata)?)
|
||||
@@ -719,10 +742,10 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn unmount(mut self) -> Result<(), Error> {
|
||||
if self.mounted {
|
||||
unmount(&self.mountpoint).await?;
|
||||
self.mounted = false;
|
||||
if let Some(guard) = self.encrypted_guard.take() {
|
||||
guard.unmount().await?;
|
||||
}
|
||||
if let Some(guard) = self.backup_disk_mount_guard.take() {
|
||||
guard.unmount().await?;
|
||||
@@ -730,6 +753,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub async fn save_and_unmount(self) -> Result<(), Error> {
|
||||
self.save().await?;
|
||||
self.unmount().await?;
|
||||
@@ -738,42 +762,63 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
}
|
||||
impl<G: GenericMountGuard> AsRef<Path> for BackupMountGuard<G> {
|
||||
fn as_ref(&self) -> &Path {
|
||||
&self.mountpoint
|
||||
if let Some(guard) = &self.encrypted_guard {
|
||||
guard.as_ref()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<G: GenericMountGuard> Drop for BackupMountGuard<G> {
|
||||
fn drop(&mut self) {
|
||||
if self.mounted {
|
||||
let mountpoint = std::mem::take(&mut self.mountpoint);
|
||||
tokio::spawn(async move { unmount(mountpoint).await.unwrap() });
|
||||
}
|
||||
let first = self.encrypted_guard.take();
|
||||
let second = self.backup_disk_mount_guard.take();
|
||||
tokio::spawn(async move {
|
||||
if let Some(guard) = first {
|
||||
guard.unmount().await.unwrap();
|
||||
}
|
||||
if let Some(guard) = second {
|
||||
guard.unmount().await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PackageBackupMountGuard {
|
||||
mountpoint: PathBuf,
|
||||
lock: FileLock,
|
||||
mounted: bool,
|
||||
mountpoint: Option<PathBuf>,
|
||||
lock: Option<FileLock>,
|
||||
}
|
||||
impl PackageBackupMountGuard {
|
||||
pub async fn unmount(mut self) -> Result<(), Error> {
|
||||
if self.mounted {
|
||||
unmount(&self.mountpoint).await?;
|
||||
self.mounted = false;
|
||||
if let Some(mountpoint) = self.mountpoint.take() {
|
||||
unmount(&mountpoint).await?;
|
||||
}
|
||||
if let Some(lock) = self.lock.take() {
|
||||
lock.unlock().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl AsRef<Path> for PackageBackupMountGuard {
|
||||
fn as_ref(&self) -> &Path {
|
||||
&self.mountpoint
|
||||
if let Some(mountpoint) = &self.mountpoint {
|
||||
mountpoint
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for PackageBackupMountGuard {
|
||||
fn drop(&mut self) {
|
||||
if self.mounted {
|
||||
let mountpoint = std::mem::take(&mut self.mountpoint);
|
||||
tokio::spawn(async move { unmount(mountpoint).await.unwrap() });
|
||||
}
|
||||
let mountpoint = self.mountpoint.take();
|
||||
let lock = self.lock.take();
|
||||
tokio::spawn(async move {
|
||||
if let Some(mountpoint) = mountpoint {
|
||||
unmount(&mountpoint).await.unwrap();
|
||||
}
|
||||
if let Some(lock) = lock {
|
||||
lock.unlock().await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user