aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hbak/src/main.rs9
-rw-r--r--hbak_common/src/error.rs3
-rw-r--r--hbak_common/src/system.rs51
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> {