This commit adds the "backup+size" export format. When this format is used, the data stream starts with metadata of the backup (protected flag & notes) followed by the contents of the backup archive.
Signed-off-by: Filip Schauer <f.scha...@proxmox.com> --- src/PVE/API2/Storage/Content.pm | 15 ++++++++++-- src/PVE/Storage.pm | 10 +++++++- src/PVE/Storage/Plugin.pm | 42 +++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/PVE/API2/Storage/Content.pm b/src/PVE/API2/Storage/Content.pm index ac451dc..9ee3c51 100644 --- a/src/PVE/API2/Storage/Content.pm +++ b/src/PVE/API2/Storage/Content.pm @@ -548,10 +548,10 @@ __PACKAGE__->register_method ({ my $cfg = PVE::Storage::config(); - my ($vtype) = PVE::Storage::parse_volname($cfg, $src_volid); + my ($vtype, undef, $ownervm) = PVE::Storage::parse_volname($cfg, $src_volid); die "use pct move-volume or qm disk move" if $vtype eq 'images' || $vtype eq 'rootdir'; die "moving volume of type '$vtype' not implemented\n" - if (!grep { $vtype eq $_ } qw(import iso snippets vztmpl)); + if (!grep { $vtype eq $_ } qw(backup import iso snippets vztmpl)); my $rpcenv = PVE::RPCEnvironment::get(); my $user = $rpcenv->get_user(); @@ -560,10 +560,21 @@ __PACKAGE__->register_method ({ if ($delete) { $rpcenv->check($user, "/storage/$src_storeid", ["Datastore.Allocate"]); + + if ($vtype eq 'backup') { + my $src_cfg = PVE::Storage::storage_config($cfg, $src_storeid); + my $src_plugin = PVE::Storage::Plugin->lookup($src_cfg->{type}); + my $protected = $src_plugin->get_volume_attribute($src_cfg, $src_storeid, $volname, 'protected'); + die "cannot delete protected backup\n" if $protected; + } } else { $rpcenv->check($user, "/storage/$dst_storeid", ["Datastore.AllocateSpace"]); } + if ($vtype eq 'backup' && $ownervm) { + $rpcenv->check($user, "/vms/$ownervm", ['VM.Backup']); + } + my $worker = sub { PVE::Storage::storage_check_enabled($cfg, $dst_storeid, $dst_node); my $sshinfo; diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm index 3b4f041..8e94979 100755 --- a/src/PVE/Storage.pm +++ b/src/PVE/Storage.pm @@ -48,7 +48,15 @@ use constant APIVER => 10; # see https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html use constant APIAGE => 1; -our $KNOWN_EXPORT_FORMATS = ['raw+size', 'tar+size', 'qcow2+size', 'vmdk+size', 'zfs', 'btrfs']; +our $KNOWN_EXPORT_FORMATS = [ + 'raw+size', + 'tar+size', + 'qcow2+size', + 'vmdk+size', + 'zfs', + 'btrfs', + 'backup+size' +]; # load standard plugins PVE::Storage::DirPlugin->register(); diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm index b682362..f335bba 100644 --- a/src/PVE/Storage/Plugin.pm +++ b/src/PVE/Storage/Plugin.pm @@ -1676,6 +1676,8 @@ sub prune_backups { # unprivileged container. In other words, this is from the root user # namespace's point of view with no uid-mapping in effect. # As produced via `tar -C vm-100-disk-1.subvol -cpf TheOutputFile.dat .` +# backup+size: (backups only) +# A raw binary data stream prefixed with a protection flag and notes. # Plugins may reuse these helpers. Changes to the header format should be # reflected by changes to the function prototypes. @@ -1723,7 +1725,18 @@ sub volume_export { die $err_msg if $file_format ne 'subvol'; write_common_header($fh, $size); run_command(['tar', @COMMON_TAR_FLAGS, '-cf', '-', '-C', $file, '.'], - output => '>&'.fileno($fh)); + output => '>&'.fileno($fh)); + return; + } elsif ($format eq 'backup+size') { + write_common_header($fh, $size); + my $protected = $class->get_volume_attribute( + $scfg, $storeid, $volname, 'protected') ? 1 : 0; + my $notes = Encode::encode( + 'utf8', $class->get_volume_attribute($scfg, $storeid, $volname, 'notes')) // ""; + syswrite($fh, pack("C", $protected), 1); + syswrite($fh, pack("Q<", length($notes)), 8); + syswrite($fh, $notes, length($notes)); + run_command(['dd', "if=$file", "bs=4k", "status=progress"], output => '>&'.fileno($fh)); return; } } @@ -1738,6 +1751,9 @@ sub volume_export_formats { my $format = ($class->parse_volname($volname))[6]; my $size = file_size_info($file, undef, $format); + my ($vtype) = $class->parse_volname($volname); + + return ('backup+size') if $vtype eq 'backup'; return ('raw+size') if !defined($format); if ($with_snapshots) { @@ -1755,7 +1771,7 @@ sub volume_import { my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots, $allow_rename) = @_; die "volume import format '$format' not available for $class\n" - if $format !~ /^(raw|tar|qcow2|vmdk)\+size$/; + if $format !~ /^(raw|tar|qcow2|vmdk|backup)\+size$/; my $data_format = $1; die "format $format cannot be imported without snapshots\n" @@ -1812,7 +1828,19 @@ sub volume_import { warn $@ if $@; die $err; } - } elsif (grep { $vtype eq $_ } qw(import iso snippets vztmpl)) { + } elsif (grep { $vtype eq $_ } qw(import iso snippets vztmpl backup)) { + my $protected; + my $notes; + + if ($vtype eq 'backup') { + sysread($fh, $protected, 1); + $protected = unpack('C', $protected); + sysread($fh, my $notes_len, 8); + $notes_len = unpack('Q<', $notes_len); + sysread($fh, $notes, $notes_len); + $notes = Encode::decode('utf8', $notes) // ""; + } + eval { run_command(['dd', "of=$file", 'bs=64k'], input => '<&'.fileno($fh)); }; @@ -1823,6 +1851,11 @@ sub volume_import { } die $err; } + + if ($vtype eq 'backup') { + $class->update_volume_attribute($scfg, $storeid, $volname, 'protected', $protected); + $class->update_volume_attribute($scfg, $storeid, $volname, 'notes', $notes); + } } else { die "importing volume of type '$vtype' not implemented\n"; } @@ -1833,8 +1866,9 @@ sub volume_import { sub volume_import_formats { my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_; if ($scfg->{path} && !defined($base_snapshot)) { - my $format = ($class->parse_volname($volname))[6]; + my ($vtype, $format) = ($class->parse_volname($volname))[0, 6]; + return ('backup+size') if $vtype eq 'backup'; return ('raw+size') if !defined($format); if ($with_snapshots) { -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel