From ca28d3ebff4c1365b3421df3727eb35d7f476fa7 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Wed, 17 May 2017 07:35:41 +0200 Subject: [PATCH] Added `copy` subcommand --- CHANGELOG.md | 4 ++++ docs/man/zvault-copy.1.md | 28 ++++++++++++++++++++++++++++ docs/man/zvault.1.md | 1 + src/cli/args.rs | 21 +++++++++++++++++++++ src/cli/mod.rs | 13 +++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 docs/man/zvault-copy.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5587e15..766213d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This project follows [semantic versioning](http://semver.org). +### UNRELEASED +* [added] Added `copy` subcommand + + ### v0.3.2 (2017-05-11) * [modified] Changed order of arguments in `addkey` to match src-dst scheme * [modified] Skip root folder on restore diff --git a/docs/man/zvault-copy.1.md b/docs/man/zvault-copy.1.md new file mode 100644 index 0000000..cd82943 --- /dev/null +++ b/docs/man/zvault-copy.1.md @@ -0,0 +1,28 @@ +zvault-copy(1) -- Create a copy of a backup +=========================================== + +## SYNOPSIS + +`zvault copy [OPTIONS] ` + + +## DESCRIPTION + +This subcommand copies the backup `SRC` to a new name `DST`. + +The backups given by `SRC` and `DST` must be in the format +`[repository]::backup_name[::subtree]` as described in _zvault(1)_. +If `repository` is omitted, the default repository location is used instead. + + +## OPTIONS + + * `-h`, `--help`: + + Prints help information + + +## COPYRIGHT + +Copyright (C) 2017 Dennis Schwerdel +This software is licensed under GPL-3 or newer (see LICENSE.md) diff --git a/docs/man/zvault.1.md b/docs/man/zvault.1.md index 678783e..ee5bea3 100644 --- a/docs/man/zvault.1.md +++ b/docs/man/zvault.1.md @@ -43,6 +43,7 @@ location. * `info` Display information on a repository, a backup or a subtree, _zvault-info(1)_ * `mount` Mount the repository, a backup or a subtree, _zvault-mount(1)_ * `remove` Remove a backup or a subtree, _zvault-remove(1)_ + * `copy` Create a copy of a backup, _zvault-copy(1)_ * `prune` Remove backups based on age, _zvault-prune(1)_ * `vacuum` Reclaim space by rewriting bundles, _zvault-vacuum(1)_ diff --git a/src/cli/args.rs b/src/cli/args.rs index dad2cb9..f61fdc7 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -74,6 +74,12 @@ pub enum Arguments { backup_name: Option, inode: Option }, + Copy { + repo_path_src: String, + backup_name_src: String, + repo_path_dst: String, + backup_name_dst: String, + }, Mount { repo_path: String, backup_name: Option, @@ -396,6 +402,11 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { .validator(|val| validate_repo_path(val, true, Some(true), None))) .arg(Arg::from_usage(" 'New version, [repository]::backup[::subpath]'") .validator(|val| validate_repo_path(val, true, Some(true), None)))) + .subcommand(SubCommand::with_name("copy").alias("cp").about("Create a copy of a backup") + .arg(Arg::from_usage(" 'Existing backup, [repository]::backup'") + .validator(|val| validate_repo_path(val, true, Some(true), Some(false)))) + .arg(Arg::from_usage(" 'Destination backup, [repository]::backup'") + .validator(|val| validate_repo_path(val, true, Some(true), Some(false))))) .subcommand(SubCommand::with_name("config").about("Display or change the configuration") .arg(Arg::from_usage("[bundle_size] --bundle-size [SIZE] 'Set the target bundle size in MiB'") .validator(validate_num)) @@ -551,6 +562,16 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> { inode: inode.map(|v| v.to_string()) } }, + ("copy", Some(args)) => { + let (repository_src, backup_src, _inode) = parse_repo_path(args.value_of("SRC").unwrap(), true, Some(true), Some(false)).unwrap(); + let (repository_dst, backup_dst, _inode) = parse_repo_path(args.value_of("DST").unwrap(), true, Some(true), Some(false)).unwrap(); + Arguments::Copy { + repo_path_src: repository_src.to_string(), + backup_name_src: backup_src.unwrap().to_string(), + repo_path_dst: repository_dst.to_string(), + backup_name_dst: backup_dst.unwrap().to_string(), + } + }, ("mount", Some(args)) => { let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); Arguments::Mount { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f2825c4..7aaaa1d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -404,6 +404,19 @@ pub fn run() -> Result<(), ErrorCode> { } info!("Restore finished"); }, + Arguments::Copy{repo_path_src, backup_name_src, repo_path_dst, backup_name_dst} => { + if repo_path_src != repo_path_dst { + error!("Can only run copy on same repository"); + return Err(ErrorCode::InvalidArgs) + } + let mut repo = try!(open_repository(&repo_path_src)); + if repo.has_backup(&backup_name_dst) { + error!("A backup with that name already exists"); + return Err(ErrorCode::BackupAlreadyExists) + } + let backup = try!(get_backup(&repo, &backup_name_src)); + checked!(repo.save_backup(&backup, &backup_name_dst), "save backup file", ErrorCode::SaveBackup); + }, Arguments::Remove{repo_path, backup_name, inode, force} => { let mut repo = try!(open_repository(&repo_path)); if let Some(inode) = inode {