Remove folders of backups (re #5)

pull/10/head
Dennis Schwerdel 2017-04-13 14:10:11 +02:00
parent 650ab331c0
commit 8a57476da3
3 changed files with 27 additions and 4 deletions

View File

@ -3,7 +3,7 @@ zvault-remove(1) -- Remove a backup or a subtree
## SYNOPSIS ## SYNOPSIS
`zvault remove <BACKUP>` `zvault remove [OPTIONS] <BACKUP>`
## DESCRIPTION ## DESCRIPTION
@ -17,6 +17,9 @@ If `repository` is omitted, the default repository location is used instead.
If a backup is referenced, this backup will be deleted. If a subtree is given, If a backup is referenced, this backup will be deleted. If a subtree is given,
the backup is instead rewritten to not include that subtree anymore. the backup is instead rewritten to not include that subtree anymore.
If a folder of backups is referenced by `BACKUP` the flag `--force` must be set
in order to remove all backups in that folder (also recursively).
Note: When removing backup subtrees, the meta information of that backup is left Note: When removing backup subtrees, the meta information of that backup is left
unchanged and still contains the data (e.g. duration and size) of the original unchanged and still contains the data (e.g. duration and size) of the original
backup run. backup run.
@ -32,6 +35,11 @@ data of the deleted backups becomes inaccessible and can not be restored.**
## OPTIONS ## OPTIONS
* `-f`, `--force`:
Remove multiple backups in a backup folder
* `-h`, `--help`: * `-h`, `--help`:
Prints help information Prints help information

View File

@ -37,7 +37,8 @@ pub enum Arguments {
Remove { Remove {
repo_path: String, repo_path: String,
backup_name: String, backup_name: String,
inode: Option<String> inode: Option<String>,
force: bool
}, },
Prune { Prune {
repo_path: String, repo_path: String,
@ -318,6 +319,7 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
.arg(Arg::from_usage("<DST> 'Destination path for backup'") .arg(Arg::from_usage("<DST> 'Destination path for backup'")
.validator(validate_existing_path))) .validator(validate_existing_path)))
.subcommand(SubCommand::with_name("remove").aliases(&["rm", "delete", "del"]).about("Remove a backup or a subtree") .subcommand(SubCommand::with_name("remove").aliases(&["rm", "delete", "del"]).about("Remove a backup or a subtree")
.arg(Arg::from_usage("-f --force 'Remove multiple backups in a backup folder'"))
.arg(Arg::from_usage("<BACKUP> 'The backup/subtree path, [repository]::backup[::subtree]'") .arg(Arg::from_usage("<BACKUP> 'The backup/subtree path, [repository]::backup[::subtree]'")
.validator(|val| validate_repo_path(val, true, Some(true), None)))) .validator(|val| validate_repo_path(val, true, Some(true), None))))
.subcommand(SubCommand::with_name("prune").about("Remove backups based on age") .subcommand(SubCommand::with_name("prune").about("Remove backups based on age")
@ -468,7 +470,8 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
Arguments::Remove { Arguments::Remove {
repo_path: repository.to_string(), repo_path: repository.to_string(),
backup_name: backup.unwrap().to_string(), backup_name: backup.unwrap().to_string(),
inode: inode.map(|v| v.to_string()) inode: inode.map(|v| v.to_string()),
force: args.is_present("force")
} }
}, },
("prune", Some(args)) => { ("prune", Some(args)) => {

View File

@ -386,13 +386,25 @@ pub fn run() -> Result<(), ErrorCode> {
} }
info!("Restore finished"); info!("Restore finished");
}, },
Arguments::Remove{repo_path, backup_name, inode} => { Arguments::Remove{repo_path, backup_name, inode, force} => {
let mut repo = try!(open_repository(&repo_path)); let mut repo = try!(open_repository(&repo_path));
if let Some(inode) = inode { if let Some(inode) = inode {
let mut backup = try!(get_backup(&repo, &backup_name)); let mut backup = try!(get_backup(&repo, &backup_name));
checked!(repo.remove_backup_path(&mut backup, inode), "remove backup subpath", ErrorCode::RemoveRun); checked!(repo.remove_backup_path(&mut backup, inode), "remove backup subpath", ErrorCode::RemoveRun);
checked!(repo.save_backup(&backup, &backup_name), "save backup file", ErrorCode::SaveBackup); checked!(repo.save_backup(&backup, &backup_name), "save backup file", ErrorCode::SaveBackup);
info!("The backup subpath has been deleted, run vacuum to reclaim space"); info!("The backup subpath has been deleted, run vacuum to reclaim space");
} else if repo.layout.backups_path().join(&backup_name).is_dir() {
let backups = checked!(repo.get_backups(&backup_name), "retrieve backups", ErrorCode::RemoveRun);
if force {
for name in backups.keys() {
checked!(repo.delete_backup(&format!("{}/{}", &backup_name, name)), "delete backup", ErrorCode::RemoveRun);
}
} else {
error!("Denying to remove multiple backups (use --force):");
for name in backups.keys() {
println!(" - {}/{}", backup_name, name);
}
}
} else { } else {
checked!(repo.delete_backup(&backup_name), "delete backup", ErrorCode::RemoveRun); checked!(repo.delete_backup(&backup_name), "delete backup", ErrorCode::RemoveRun);
info!("The backup has been deleted, run vacuum to reclaim space"); info!("The backup has been deleted, run vacuum to reclaim space");