use strict;
use warnings;
+use Encode qw(decode);
use Fcntl ':mode';
use File::chdir;
use File::Path;
optional => 1,
},
'prune-backups' => get_standard_option('prune-backups'),
+ 'max-protected-backups' => {
+ description => "Maximal number of protected backups per guest. Use '-1' for unlimited.",
+ type => 'integer',
+ minimum => -1,
+ optional => 1,
+ default => "Unlimited for users with Datastore.Allocate privilege, 5 for other users",
+ },
shared => {
description => "Mark storage as shared.",
type => 'boolean',
my ($vmid, $name) = ($1, $2);
my (undef, $format, $isBase) = parse_name_dir($name);
return ('images', $name, $vmid, undef, undef, $isBase, $format);
- } elsif ($volname =~ m!^iso/([^/]+$PVE::Storage::iso_extension_re)$!) {
+ } elsif ($volname =~ m!^iso/([^/]+$PVE::Storage::ISO_EXT_RE_0)$!) {
return ('iso', $1);
- } elsif ($volname =~ m!^vztmpl/([^/]+$PVE::Storage::vztmpl_extension_re)$!) {
+ } elsif ($volname =~ m!^vztmpl/([^/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!) {
return ('vztmpl', $1);
} elsif ($volname =~ m!^rootdir/(\d+)$!) {
return ('rootdir', $1, $1);
- } elsif ($volname =~ m!^backup/([^/]+(?:\.(?:tgz|(?:(?:tar|vma)(?:\.(?:${\COMPRESSOR_RE}))?))))$!) {
+ } elsif ($volname =~ m!^backup/([^/]+$PVE::Storage::BACKUP_EXT_RE_2)$!) {
my $fn = $1;
if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
return ('backup', $fn, $2);
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running, $opts) = @_;
my $features = {
- snapshot => { current => { qcow2 => 1}, snap => { qcow2 => 1} },
- clone => { base => {qcow2 => 1, raw => 1, vmdk => 1} },
- template => { current => {qcow2 => 1, raw => 1, vmdk => 1, subvol => 1} },
- copy => { base => {qcow2 => 1, raw => 1, vmdk => 1},
- current => {qcow2 => 1, raw => 1, vmdk => 1},
- snap => {qcow2 => 1} },
- sparseinit => { base => {qcow2 => 1, raw => 1, vmdk => 1},
- current => {qcow2 => 1, raw => 1, vmdk => 1} },
- rename => { current => {qcow2 => 1, raw => 1, vmdk => 1} },
+ snapshot => {
+ current => { qcow2 => 1 },
+ snap => { qcow2 => 1 },
+ },
+ clone => {
+ base => { qcow2 => 1, raw => 1, vmdk => 1 },
+ },
+ template => {
+ current => { qcow2 => 1, raw => 1, vmdk => 1, subvol => 1 },
+ },
+ copy => {
+ base => { qcow2 => 1, raw => 1, vmdk => 1 },
+ current => { qcow2 => 1, raw => 1, vmdk => 1 },
+ snap => { qcow2 => 1 },
+ },
+ sparseinit => {
+ base => { qcow2 => 1, raw => 1, vmdk => 1 },
+ current => { qcow2 => 1, raw => 1, vmdk => 1 },
+ },
+ rename => {
+ current => {qcow2 => 1, raw => 1, vmdk => 1},
+ },
};
- # clone_image creates a qcow2 volume
- return 0 if $feature eq 'clone' &&
- defined($opts->{valid_target_formats}) &&
- !(grep { $_ eq 'qcow2' } @{$opts->{valid_target_formats}});
+ if ($feature eq 'clone') {
+ if (
+ defined($opts->{valid_target_formats})
+ && !(grep { $_ eq 'qcow2' } @{$opts->{valid_target_formats}})
+ ) {
+ return 0; # clone_image creates a qcow2 volume
+ }
+ } elsif ($feature eq 'rename') {
+ return 0 if $class->can('api') && $class->api() < 10;
+ }
- return 0 if $feature eq 'rename' && $class->can('api') && $class->api() < 10;
- my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
- $class->parse_volname($volname);
+ my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) = $class->parse_volname($volname);
my $key = undef;
if($snapname){
my $info;
if ($tt eq 'iso') {
- next if $fn !~ m!/([^/]+$PVE::Storage::iso_extension_re)$!i;
+ next if $fn !~ m!/([^/]+$PVE::Storage::ISO_EXT_RE_0)$!i;
$info = { volid => "$sid:iso/$1", format => 'iso' };
} elsif ($tt eq 'vztmpl') {
- next if $fn !~ m!/([^/]+$PVE::Storage::vztmpl_extension_re)$!;
+ next if $fn !~ m!/([^/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!;
$info = { volid => "$sid:vztmpl/$1", format => "t$2" };
} elsif ($tt eq 'backup') {
- next if $fn !~ m!/([^/]+\.(tgz|(?:(?:tar|vma)(?:\.(${\COMPRESSOR_RE}))?)))$!;
+ next if $fn !~ m!/([^/]+$PVE::Storage::BACKUP_EXT_RE_2)$!;
my $original = $fn;
my $format = $2;
$fn = $1;
my $archive_info = eval { PVE::Storage::archive_info($fn) } // {};
$info->{ctime} = $archive_info->{ctime} if defined($archive_info->{ctime});
+ $info->{subtype} = $archive_info->{type} // 'unknown';
if (defined($vmid) || $fn =~ m!\-([1-9][0-9]{2,8})\-[^/]+\.${format}$!) {
$info->{vmid} = $vmid // $1;
my $notes_fn = $original.NOTES_EXT;
if (-f $notes_fn) {
my $notes = PVE::Tools::file_read_firstline($notes_fn);
- $info->{notes} = $notes if defined($notes);
+ $info->{notes} = eval { decode('UTF-8', $notes, 1) } // $notes if defined($notes);
}
$info->{protected} = 1 if -e PVE::Storage::protection_file_path($original);