diff options
-rw-r--r-- | hbak/src/main.rs | 9 | ||||
-rw-r--r-- | hbak_common/src/error.rs | 3 | ||||
-rw-r--r-- | hbak_common/src/system.rs | 51 |
3 files changed, 63 insertions, 0 deletions
diff --git a/hbak/src/main.rs b/hbak/src/main.rs index 2a656d7..1516426 100644 --- a/hbak/src/main.rs +++ b/hbak/src/main.rs @@ -27,6 +27,12 @@ enum Commands { /// The network address `hbakd` binds to. The default is `[::]:20406` (dual stack). bind_addr: Option<SocketAddr>, }, + /// Fully clean the local node of non-binary files with optional backup removal. + Clean { + /// Remove the btrfs subvolumes that contain the snapshots and backups. + #[arg(short = 'b', long = "backups")] + backups: bool, + }, /// Mark a subvolume as owned by the local node. Track { /// The name of the subvolume to mark as owned. @@ -107,6 +113,9 @@ fn logic() -> Result<()> { let passphrase = rpassword::prompt_password("Enter new encryption passphrase: ")?; system::init(device, bind_addr, node_name, passphrase)?; } + Commands::Clean { backups } => { + system::deinit(backups)?; + } Commands::Track { subvol } => { let mut node_config = NodeConfig::load()?; diff --git a/hbak_common/src/error.rs b/hbak_common/src/error.rs index ab865ad..a17a065 100644 --- a/hbak_common/src/error.rs +++ b/hbak_common/src/error.rs @@ -78,6 +78,9 @@ pub enum LocalNodeError { /// A configuration file already exists on this node. #[error("Local node is already initialized")] ConfigExists, + /// No configuration file exists on this node. + #[error("Local node is not initialized")] + ConfigUninit, /// The permissions on the configuration file are insecure. #[error("Insecure config permissions (limit access to root user!)")] InsecurePerms, diff --git a/hbak_common/src/system.rs b/hbak_common/src/system.rs index f7c0297..3fd48c8 100644 --- a/hbak_common/src/system.rs +++ b/hbak_common/src/system.rs @@ -76,6 +76,57 @@ fn init_btrfs(device: &str) -> Result<(), LocalNodeError> { Ok(()) } +/// Deinitializes the configuration file, optionally deleting the btrfs subvolumes. +pub fn deinit(remove_backups: bool) -> Result<(), LocalNodeError> { + if !Path::new(NodeConfig::PATH).exists() { + return Err(LocalNodeError::ConfigUninit); + } + + if remove_backups { + deinit_btrfs()?; + } + + fs::remove_file(NodeConfig::PATH)?; + + Ok(()) +} + +fn deinit_btrfs() -> Result<(), LocalNodeError> { + fs::create_dir_all(MOUNTPOINT)?; + + let node_config = NodeConfig::load()?; + + let _btrfs = Mount::builder().data("compress=zstd").mount_autodrop( + node_config.device, + MOUNTPOINT, + UnmountFlags::DETACH, + )?; + + if !Command::new("btrfs") + .arg("subvolume") + .arg("delete") + .arg(BACKUP_DIR) + .spawn()? + .wait()? + .success() + { + return Err(LocalNodeError::BtrfsCmd); + } + + if !Command::new("btrfs") + .arg("subvolume") + .arg("delete") + .arg(SNAPSHOT_DIR) + .spawn()? + .wait()? + .success() + { + return Err(LocalNodeError::BtrfsCmd); + } + + Ok(()) +} + /// Provides a `Vec<u8>` of `n` random bytes. Uses the thread-local generator /// of the `rand` crate. pub fn random_bytes(n: usize) -> Vec<u8> { |