use std::{
collections::BTreeSet,
path::{Path, PathBuf},
};
use tracing::{info, warn};
use super::sync::Decide;
use crate::actions::canopy::backup::hold::{DivergenceMark, HoldRecord};
#[cfg(unix)]
mod btrfs;
#[cfg(unix)]
mod lvm;
#[derive(Debug)]
pub enum Basis {
Named {
paths: BTreeSet<PathBuf>,
how: &'static str,
},
Unavailable {
why: String,
},
}
impl Basis {
pub fn into_decision(self) -> Decide {
match self {
Self::Named { paths, .. } => Decide::Named(paths),
Self::Unavailable { .. } => Decide::Compare,
}
}
pub fn is_named(&self) -> bool {
matches!(self, Self::Named { .. })
}
fn unavailable(why: impl Into<String>) -> Self {
Self::Unavailable { why: why.into() }
}
}
pub async fn resolve(record: &HoldRecord, live: &Path) -> Basis {
let basis = match &record.diverged_since {
#[cfg(unix)]
Some(DivergenceMark::BtrfsGeneration {
generation,
subvolume,
}) => match subvolume {
Some(subvolume) => btrfs::changed_since(*generation, subvolume, live).await,
None => Basis::unavailable(
"the capture recorded a btrfs generation without the subvolume it was counted on",
),
},
#[cfg(windows)]
Some(DivergenceMark::UsnJournal {
volume,
journal_id,
usn,
}) => {
let position = crate::actions::canopy::backup::postgresql::usn::Position {
journal_id: *journal_id,
usn: *usn,
};
match crate::actions::canopy::backup::postgresql::usn::changed_since(
volume, position, live,
)
.await
{
Some(paths) => Basis::Named {
paths,
how: "the NTFS change journal",
},
None => Basis::unavailable("the NTFS change journal could not answer"),
}
}
#[cfg(not(unix))]
Some(DivergenceMark::BtrfsGeneration { .. }) => {
Basis::unavailable("the capture's btrfs generation cannot be read here")
}
#[cfg(not(windows))]
Some(DivergenceMark::UsnJournal { .. }) => {
Basis::unavailable("the capture's change journal cannot be read here")
}
None => Basis::unavailable(format!(
"the {} capture recorded no position in its filesystem's change history",
record.capture.backend()
)),
};
match &basis {
Basis::Named { paths, how } => info!(
changed = paths.len(),
"{how} named what diverged from the capture"
),
Basis::Unavailable { why } => warn!(
"{why}, so the restore compared the trees; files whose size and \
modification time both survived a change are kept as they are"
),
}
basis
}
#[cfg(unix)]
pub async fn divergence_bytes(
record: &HoldRecord,
live: &Path,
pool: Option<&super::blockdev::ThinPool>,
) -> Option<u64> {
lvm::diverged_bytes(record, live, pool?).await
}
#[cfg(not(unix))]
pub async fn divergence_bytes(record: &HoldRecord, live: &Path) -> Option<u64> {
let _ = (record, live);
None
}