Huge refactor, quicksave

This commit is contained in:
2026-09-17 21:54:20 +02:00
parent d2ddceb1a2
commit e4febb1e60
4 changed files with 571 additions and 139 deletions
+251 -12
View File
@@ -22,9 +22,26 @@ use nix;
use std::fs;
use std::process::Command;
// timeout for block deices population
/// timeout for block deices population
const WAIT_FOR_BLK_DEVS: u64 = 5u64;
/// list of subvolumes to move from current system to snapshoot when being restored
const SBUVOL_TO_MOVE: [(&str, &str); 1] = [("var", "/tmp")];
const TMP_ROOT: &str = "/mnt/bee-root";
const TMP_ROOT_SNAPS: &str = "/mnt/bee-root/bzz-snaps";
const TMP_ROOT_USR: &str = "/mnt/bee-root/usr";
const TMP_ROOT_VAR: &str = "/mnt/bee-root/var";
/// Path where /usr subvolume got moved on snapshot restore
const TMP_ROOT_SNAPS_USR: &str = "/mnt/bee-root/bzz-snaps/tmp_usr";
/// Path where /var subvolume got moved on snapshot restore
const TMP_ROOT_SNAPS_VAR: &str = "/mnt/bee-root/bzz-snaps/tmp_var";
const TMP_ROOT_SNAPS_PREFIX: &str = "/mnt/bee-root/bzz-snaps/tmp_";
const PAC_LOCK_FILE: &str = "/var/lib/hive-up/pac-lock";
#[derive(Debug)]
pub struct CommandFailed {
pub stderr: Vec<u8>,
@@ -249,13 +266,17 @@ pub fn bzzpss_dev_encrypted(dev_name: &str) -> Result<bool, ExecCommandError> {
}
}
//const TMP_ROOT: &str = "/mnt/bee-root";
///
/// Looking for snapshots in snaps_dir for usr and var
/// And return list of date-time part of filename if both snapshots exists
///
pub fn list_bzzpss_snaps(snaps_dir: &str) -> Result<Vec<String>, ExecCommandError> {
use regex_lite::Regex;
use std::path::Path;
// root snapshoot in a form of bzz-yyyy-mm-ddTHH-MM-SS
let re = Regex::new(r"^bzz-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}$").unwrap();
// usr or var snapshoot in a form of bzz-usr|var-yyyy-mm-ddTHH-MM-SS
let re = Regex::new(r"^bzz-(usr|var)-([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2})$")
.unwrap();
let mut res: Vec<String> = Vec::new();
// if snaps dir path do not exist return empty list
@@ -263,19 +284,231 @@ pub fn list_bzzpss_snaps(snaps_dir: &str) -> Result<Vec<String>, ExecCommandErro
return Ok(res);
}
// contains only date-time part
let mut usr_snaps: Vec<String> = Vec::new();
let mut var_snaps: Vec<String> = Vec::new();
// collect snapshots date-time
for maybe_entry in fs::read_dir(snaps_dir)? {
let entry = maybe_entry?;
if entry.file_type()?.is_dir() {
if let Some(dir_name) = entry.file_name().to_str()
&& re.is_match(dir_name)
{
res.push(dir_name.to_string());
if let Some(dir_name) = entry.file_name().to_str() {
if let Some(captured) = re.captures(&dir_name) {
let subvol_name = &captured[1];
let date_time = captured[2].to_string();
if subvol_name == "usr" {
usr_snaps.push(date_time);
} else {
var_snaps.push(date_time);
}
}
}
}
}
// now we need to make sure snapshots of /usr and /var both exist
// just fools safe, may reconsider in future
usr_snaps.sort();
for snap_time in usr_snaps {
if var_snaps.contains(&snap_time) {
res.push(snap_time);
}
}
Ok(res)
}
///
/// creates rw snapshot of "subvolume" and places it into "target"
/// bcachefs subvolume snapshot --rw /subvolume /target
///
pub fn snapshot_rw(subvolume: &str, target: &str) -> Result<(), ExecCommandError> {
exec_command(
"bcachefs",
&vec!["subvolume", "snapshot", "--rw", subvolume, target],
)?;
Ok(())
}
///
/// bcachefs subvolume delete shortcut
///
pub fn subvolume_delete(subvolume: &str) -> Result<(), ExecCommandError> {
exec_command("bcachefs", &vec!["subvolume", "delete", subvolume])?;
Ok(())
}
///
/// Helper function to get snapshot names in form of "bzz-(usr|var)-date-time"
/// vol: "usr" or "var"
/// dt: datetime
///
fn snap_name_from_dt(vol: &str, dt: &str) -> String {
format!("bzz-{}-{}", vol, dt)
}
///
///
/// // Stable Rust example using from_fn
fn first_n(n: u64) -> impl Iterator<Item = u64> {
let mut count = 0;
std::iter::from_fn(move || {
if count < n {
let val = count;
count += 1;
Some(val)
} else {
None
}
})
}
enum RollbackStates {
Start,
RenameUsrVarAct,
ChangeToRw,
MoveSubvolums,
RemoveOldVols,
ClearSnaps,
Finish,
Err,
}
pub fn roll_back_snap(
snaps_list: &Vec<String>,
target_snap: &str,
) -> impl Iterator<Item = Result<String, ExecCommandError>> {
let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap);
let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap);
let mut roll_state = RollbackStates::Start;
std::iter::from_fn(move || match roll_state {
RollbackStates::Start => {
roll_state = RollbackStates::RenameUsrVarAct;
Some(Ok("Move usr and var to temporary location".into()))
}
RollbackStates::RenameUsrVarAct => {
let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)
.and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR));
if let Err(ex) = move_res {
roll_state = RollbackStates::Err;
Some(Err(ex.into()))
} else {
roll_state = RollbackStates::ChangeToRw;
Some(Ok("Setting snapshot as current system ...".into()))
}
}
RollbackStates::ChangeToRw => {
let rw_res = snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)
.and_then(|_| snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR));
if let Err(ex) = rw_res {
roll_state = RollbackStates::Err;
Some(Err(ex))
} else {
roll_state = RollbackStates::MoveSubvolums;
Some(Ok(
"Moving subvolumes from old /usr and /var to new one".into()
))
}
}
RollbackStates::MoveSubvolums => {
let mut mv_res = Some(Ok("Removing old /usr and /var".into()));
for (vol, svol_path) in SBUVOL_TO_MOVE {
let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path);
let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path);
if let Err(ex) = fs::rename(v_from, v_dest) {
roll_state = RollbackStates::Err;
mv_res = Some(Err(ex.into()));
break;
}
}
roll_state = RollbackStates::RemoveOldVols;
mv_res
}
RollbackStates::RemoveOldVols => {
let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR)
.and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR));
if let Err(ex) = rem_old_res {
roll_state = RollbackStates::Err;
Some(Err(ex))
} else {
roll_state = RollbackStates::ClearSnaps;
Some(Ok("Clearing snapshots...".into()))
}
}
RollbackStates::ClearSnaps => {
let mut clr_snap_res = Some(Ok("All done!".into()));
for snap_date_time in snaps_list.iter().rev() {
let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
if let Err(ex) =
subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap))
{
clr_snap_res = Some(Err(ex));
roll_state = RollbackStates::Err;
break;
}
if snap_date_time == target_snap {
break;
}
}
clr_snap_res
}
RollbackStates::Finish => None,
RollbackStates::Err => None,
})
// 1. Rename usr and var to TMP_ROOT_SNAPS/tmp_var|usr
// fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)?;
// fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)?;
// 2. snapshot desired snaps as RW to /usr and /var
// snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)?;
// snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)?;
// 3. move subvolumes from old /usr and /var
// for (vol, svol_path) in SBUVOL_TO_MOVE {
// let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path);
// let v_dest = format!("{}/{}{}",TMP_ROOT,vol,svol_path);
// fs::rename(v_from, v_dest)?;
// }
// 4. removing tmp /usr /var
// subvolume_delete(TMP_ROOT_SNAPS_USR)?;
// subvolume_delete(TMP_ROOT_SNAPS_VAR)?;
// 5. Removing all snapshoots made after one we restored, including inself
// Iterate in reverse untill we get to target_snap in snaps list
// for snap_date_time in snaps_list.iter().rev() {
// let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// subvolume_delete(&usr_snap)?;
// subvolume_delete(&var_snap)?;
// if snap_date_time == target_snap {
// break;
// }
// }
// Ok(())
}
///
/// Checks if pac_lock file exist and returns date-time from it if it does,
/// Otherwise return None
///
pub fn check_pac_lock() -> Result<Option<String>, ExecCommandError> {
use std::path::Path;
let pac_lock_path = Path::new(PAC_LOCK_FILE);
if pac_lock_path.exists() {
let snap_dt = fs::read_to_string(pac_lock_path)?;
Ok(Some(snap_dt.trim().into()))
}
else {
Ok(None)
}
}
// pulls /sys/class/block
fn wait_for_block_devices(timeout_secs: u64) -> bool {
use std::path::Path;
@@ -300,7 +533,6 @@ fn wait_for_block_devices(timeout_secs: u64) -> bool {
false
}
// mounting process
//# Mount essential virtual filesystems
// mount -t proc none /proc
@@ -351,7 +583,8 @@ pub fn prepare_env() -> Result<(), ExecCommandError> {
.map_err(|ex| format!("Error mounting /dev: {}", ex.to_string()))?;
//mount -t tmpfs -o nosuid,nodev,noexec,mode=0755 none /run
let run_mount_flags = MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME;
let run_mount_flags =
MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME;
mount(
Some("run"),
"/run",
@@ -367,7 +600,6 @@ pub fn prepare_env() -> Result<(), ExecCommandError> {
// Creating dir for udev so this little shit wont hang
// fs::create_dir("/run/udev")?;
Ok(())
}
@@ -412,7 +644,14 @@ pub fn mount_bee_root(dev_name: &str, mount_point: &str) -> Result<(), ExecComma
root_mount_flags,
None::<&str>,
)
.map_err(|ex| format!("Error mounting root {} to {} :\n{}",dev_name, mount_point, ex.to_string()))?;
.map_err(|ex| {
format!(
"Error mounting root {} to {} :\n{}",
dev_name,
mount_point,
ex.to_string()
)
})?;
// exec_command("/usr/bin/mount", &vec!["-o", "ro", dev_name, mount_point])?;
Ok(())
}