Added `copy` subcommand

This commit is contained in:
Dennis Schwerdel 2017-05-17 07:35:41 +02:00
parent 148db7d627
commit ca28d3ebff
5 changed files with 67 additions and 0 deletions

View File

@ -3,6 +3,10 @@
This project follows [semantic versioning](http://semver.org). This project follows [semantic versioning](http://semver.org).
### UNRELEASED
* [added] Added `copy` subcommand
### v0.3.2 (2017-05-11) ### v0.3.2 (2017-05-11)
* [modified] Changed order of arguments in `addkey` to match src-dst scheme * [modified] Changed order of arguments in `addkey` to match src-dst scheme
* [modified] Skip root folder on restore * [modified] Skip root folder on restore

28
docs/man/zvault-copy.1.md Normal file
View File

@ -0,0 +1,28 @@
zvault-copy(1) -- Create a copy of a backup
===========================================
## SYNOPSIS
`zvault copy [OPTIONS] <SRC> <DST>`
## 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)

View File

@ -43,6 +43,7 @@ location.
* `info` Display information on a repository, a backup or a subtree, _zvault-info(1)_ * `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)_ * `mount` Mount the repository, a backup or a subtree, _zvault-mount(1)_
* `remove` Remove a backup or a subtree, _zvault-remove(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)_ * `prune` Remove backups based on age, _zvault-prune(1)_
* `vacuum` Reclaim space by rewriting bundles, _zvault-vacuum(1)_ * `vacuum` Reclaim space by rewriting bundles, _zvault-vacuum(1)_

View File

@ -74,6 +74,12 @@ pub enum Arguments {
backup_name: Option<String>, backup_name: Option<String>,
inode: Option<String> inode: Option<String>
}, },
Copy {
repo_path_src: String,
backup_name_src: String,
repo_path_dst: String,
backup_name_dst: String,
},
Mount { Mount {
repo_path: String, repo_path: String,
backup_name: Option<String>, backup_name: Option<String>,
@ -396,6 +402,11 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
.validator(|val| validate_repo_path(val, true, Some(true), None))) .validator(|val| validate_repo_path(val, true, Some(true), None)))
.arg(Arg::from_usage("<NEW> 'New version, [repository]::backup[::subpath]'") .arg(Arg::from_usage("<NEW> 'New version, [repository]::backup[::subpath]'")
.validator(|val| validate_repo_path(val, true, Some(true), None)))) .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("<SRC> 'Existing backup, [repository]::backup'")
.validator(|val| validate_repo_path(val, true, Some(true), Some(false))))
.arg(Arg::from_usage("<DST> '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") .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'") .arg(Arg::from_usage("[bundle_size] --bundle-size [SIZE] 'Set the target bundle size in MiB'")
.validator(validate_num)) .validator(validate_num))
@ -551,6 +562,16 @@ pub fn parse() -> Result<(LogLevel, Arguments), ErrorCode> {
inode: inode.map(|v| v.to_string()) 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)) => { ("mount", Some(args)) => {
let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap(); let (repository, backup, inode) = parse_repo_path(args.value_of("PATH").unwrap(), true, None, None).unwrap();
Arguments::Mount { Arguments::Mount {

View File

@ -404,6 +404,19 @@ pub fn run() -> Result<(), ErrorCode> {
} }
info!("Restore finished"); 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} => { 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 {