X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FStorage.pm;h=223fbb5b4e94cfc6fe3823507dd234c116012095;hb=9a4c0e84713d154539accad94c421a160c211e24;hp=f2231f2f8c67beb40df19cc9ad907dc2badbbb72;hpb=823e8afe72a84f3abff884bd4b5c32d140ffdf62;p=pve-storage.git diff --git a/PVE/Storage.pm b/PVE/Storage.pm index f2231f2..223fbb5 100755 --- a/PVE/Storage.pm +++ b/PVE/Storage.pm @@ -38,13 +38,14 @@ use PVE::Storage::GlusterfsPlugin; use PVE::Storage::ZFSPoolPlugin; use PVE::Storage::ZFSPlugin; use PVE::Storage::PBSPlugin; +use PVE::Storage::BTRFSPlugin; # Storage API version. Increment it on changes in storage API interface. -use constant APIVER => 8; +use constant APIVER => 10; # Age is the number of versions we're backward compatible with. # This is like having 'current=APIVER' and age='APIAGE' in libtool, # see https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html -use constant APIAGE => 7; +use constant APIAGE => 1; # load standard plugins PVE::Storage::DirPlugin->register(); @@ -60,6 +61,7 @@ PVE::Storage::GlusterfsPlugin->register(); PVE::Storage::ZFSPoolPlugin->register(); PVE::Storage::ZFSPlugin->register(); PVE::Storage::PBSPlugin->register(); +PVE::Storage::BTRFSPlugin->register(); # load third-party plugins if ( -d '/usr/share/perl5/PVE/Storage/Custom' ) { @@ -101,6 +103,8 @@ PVE::Storage::Plugin->init(); our $iso_extension_re = qr/\.(?:iso|img)/i; +our $vztmpl_extension_re = qr/\.tar\.(gz|xz|zst)/i; + # PVE::Storage utility functions sub config { @@ -190,7 +194,7 @@ sub storage_check_enabled { # storage_can_replicate: # return true if storage supports replication -# (volumes alocated with vdisk_alloc() has replication feature) +# (volumes allocated with vdisk_alloc() has replication feature) sub storage_can_replicate { my ($cfg, $storeid, $format) = @_; @@ -211,24 +215,24 @@ sub file_size_info { return PVE::Storage::Plugin::file_size_info($filename, $timeout); } -sub get_volume_notes { - my ($cfg, $volid, $timeout) = @_; +sub get_volume_attribute { + my ($cfg, $volid, $attribute) = @_; my ($storeid, $volname) = parse_volume_id($volid); my $scfg = storage_config($cfg, $storeid); my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - return $plugin->get_volume_notes($scfg, $storeid, $volname, $timeout); + return $plugin->get_volume_attribute($scfg, $storeid, $volname, $attribute); } -sub update_volume_notes { - my ($cfg, $volid, $notes, $timeout) = @_; +sub update_volume_attribute { + my ($cfg, $volid, $attribute, $value) = @_; my ($storeid, $volname) = parse_volume_id($volid); my $scfg = storage_config($cfg, $storeid); my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - $plugin->update_volume_notes($scfg, $storeid, $volname, $notes, $timeout); + return $plugin->update_volume_attribute($scfg, $storeid, $volname, $attribute, $value); } sub volume_size_info { @@ -265,13 +269,13 @@ sub volume_resize { } sub volume_rollback_is_possible { - my ($cfg, $volid, $snap) = @_; + my ($cfg, $volid, $snap, $blockers) = @_; my ($storeid, $volname) = parse_volume_id($volid, 1); if ($storeid) { my $scfg = storage_config($cfg, $storeid); my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - return $plugin->volume_rollback_is_possible($scfg, $storeid, $volname, $snap); + return $plugin->volume_rollback_is_possible($scfg, $storeid, $volname, $snap, $blockers); } elsif ($volid =~ m|^(/.+)$| && -e $volid) { die "snapshot rollback file/device '$volid' is not possible\n"; } else { @@ -367,20 +371,13 @@ sub volume_has_feature { } } -sub volume_snapshot_list { +sub volume_snapshot_info { my ($cfg, $volid) = @_; - my ($storeid, $volname) = parse_volume_id($volid, 1); - if ($storeid) { - my $scfg = storage_config($cfg, $storeid); - my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - return $plugin->volume_snapshot_list($scfg, $storeid, $volname); - } elsif ($volid =~ m|^(/.+)$| && -e $volid) { - die "send file/device '$volid' is not possible\n"; - } else { - die "unable to parse volume ID '$volid'\n"; - } - # return an empty array if dataset does not exist. + my ($storeid, $volname) = parse_volume_id($volid); + my $scfg = storage_config($cfg, $storeid); + my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); + return $plugin->volume_snapshot_info($scfg, $storeid, $volname); } sub get_image_dir { @@ -573,7 +570,7 @@ sub path_to_volume_id { } elsif ($path =~ m!^$isodir/([^/]+$iso_extension_re)$!) { my $name = $1; return ('iso', "$sid:iso/$name"); - } elsif ($path =~ m!^$tmpldir/([^/]+\.tar\.gz)$!) { + } elsif ($path =~ m!^$tmpldir/([^/]+$vztmpl_extension_re)$!) { my $name = $1; return ('vztmpl', "$sid:vztmpl/$name"); } elsif ($path =~ m!^$privatedir/(\d+)$!) { @@ -688,7 +685,7 @@ sub storage_migrate { my $migration_snapshot; if (!defined($snapshot)) { - if ($scfg->{type} eq 'zfspool') { + if ($scfg->{type} eq 'zfspool' || $scfg->{type} eq 'btrfs') { $migration_snapshot = 1; $snapshot = '__migration__'; } @@ -712,7 +709,8 @@ sub storage_migrate { my $send = ['pvesm', 'export', $volid, $format, '-', '-with-snapshots', $with_snapshots]; my $recv = [@$ssh, '--', 'pvesm', 'import', $target_volid, $format, $import_fn, '-with-snapshots', $with_snapshots]; if (defined($snapshot)) { - push @$send, '-snapshot', $snapshot + push @$send, '-snapshot', $snapshot; + push @$recv, '-snapshot', $snapshot; } if ($migration_snapshot) { push @$recv, '-delete-snapshot', $snapshot; @@ -722,7 +720,7 @@ sub storage_migrate { if (defined($base_snapshot)) { # Check if the snapshot exists on the remote side: push @$send, '-base', $base_snapshot; - push @$recv, '-base', $base_snapshot; + push @$recv, '-base', $base_snapshot if $target_apiver >= 9; } my $new_volid; @@ -934,7 +932,7 @@ sub vdisk_list { storage_check_enabled($cfg, $storeid) if ($storeid); - my $res = {}; + my $res = $storeid ? { $storeid => [] } : {}; # prepare/activate/refresh all storages @@ -961,12 +959,8 @@ sub vdisk_list { activate_storage_list($cfg, $storage_list, $cache); - # FIXME PVE 7.0: only scan storages with the correct content types - my $scan = defined($ctype) ? $storage_list : [ keys %{$ids} ]; - - foreach my $sid (@{$scan}) { + for my $sid ($storage_list->@*) { next if $storeid && $storeid ne $sid; - next if !storage_check_enabled($cfg, $sid, undef, 1); my $scfg = $ids->{$sid}; my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); @@ -1559,7 +1553,7 @@ sub extract_vzdump_config_vma { my $errstring; my $err = sub { my $output = shift; - if ($output =~ m/lzop: Broken pipe: / || $output =~ m/gzip: stdout: Broken pipe/ || $output =~ m/zstd: error 70 : Write error : Broken pipe/) { + if ($output =~ m/lzop: Broken pipe: / || $output =~ m/gzip: stdout: Broken pipe/ || $output =~ m/zstd: error 70 : Write error.*Broken pipe/) { $broken_pipe = 1; } elsif (!defined ($errstring) && $output !~ m/^\s*$/) { $errstring = "Failed to extract config from VMA archive: $output\n"; @@ -1665,11 +1659,12 @@ my $prune_mark = sub { sub prune_mark_backup_group { my ($backup_group, $keep) = @_; - my $keep_all = delete $keep->{'keep-all'}; + my @positive_opts = grep { $_ ne 'keep-all' && $keep->{$_} > 0 } keys $keep->%*; - if ($keep_all || !scalar(grep {$_ > 0} values %{$keep})) { - $keep = { 'keep-all' => 1 } if $keep_all; + if ($keep->{'keep-all'} || scalar(@positive_opts) == 0) { foreach my $prune_entry (@{$backup_group}) { + # preserve additional information like 'protected' + next if $prune_entry->{mark} && $prune_entry->{mark} ne 'remove'; $prune_entry->{mark} = 'keep'; } return; @@ -1714,7 +1709,7 @@ sub prune_mark_backup_group { } } -sub volume_export { +sub volume_export : prototype($$$$$$$) { my ($cfg, $fh, $volid, $format, $snapshot, $base_snapshot, $with_snapshots) = @_; my ($storeid, $volname) = parse_volume_id($volid, 1); @@ -1725,18 +1720,27 @@ sub volume_export { $snapshot, $base_snapshot, $with_snapshots); } -sub volume_import { - my ($cfg, $fh, $volid, $format, $base_snapshot, $with_snapshots, $allow_rename) = @_; +sub volume_import : prototype($$$$$$$$) { + my ($cfg, $fh, $volid, $format, $snapshot, $base_snapshot, $with_snapshots, $allow_rename) = @_; my ($storeid, $volname) = parse_volume_id($volid, 1); die "cannot import into volume '$volid'\n" if !$storeid; my $scfg = storage_config($cfg, $storeid); my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - return $plugin->volume_import($scfg, $storeid, $fh, $volname, $format, - $base_snapshot, $with_snapshots, $allow_rename) // $volid; -} - -sub volume_export_formats { + return $plugin->volume_import( + $scfg, + $storeid, + $fh, + $volname, + $format, + $snapshot, + $base_snapshot, + $with_snapshots, + $allow_rename, + ) // $volid; +} + +sub volume_export_formats : prototype($$$$$) { my ($cfg, $volid, $snapshot, $base_snapshot, $with_snapshots) = @_; my ($storeid, $volname) = parse_volume_id($volid, 1); @@ -1748,21 +1752,27 @@ sub volume_export_formats { $with_snapshots); } -sub volume_import_formats { - my ($cfg, $volid, $base_snapshot, $with_snapshots) = @_; +sub volume_import_formats : prototype($$$$$) { + my ($cfg, $volid, $snapshot, $base_snapshot, $with_snapshots) = @_; my ($storeid, $volname) = parse_volume_id($volid, 1); return if !$storeid; my $scfg = storage_config($cfg, $storeid); my $plugin = PVE::Storage::Plugin->lookup($scfg->{type}); - return $plugin->volume_import_formats($scfg, $storeid, $volname, - $base_snapshot, $with_snapshots); + return $plugin->volume_import_formats( + $scfg, + $storeid, + $volname, + $snapshot, + $base_snapshot, + $with_snapshots, + ); } sub volume_transfer_formats { my ($cfg, $src_volid, $dst_volid, $snapshot, $base_snapshot, $with_snapshots) = @_; my @export_formats = volume_export_formats($cfg, $src_volid, $snapshot, $base_snapshot, $with_snapshots); - my @import_formats = volume_import_formats($cfg, $dst_volid, $base_snapshot, $with_snapshots); + my @import_formats = volume_import_formats($cfg, $dst_volid, $snapshot, $base_snapshot, $with_snapshots); my %import_hash = map { $_ => 1 } @import_formats; my @common = grep { $import_hash{$_} } @export_formats; return @common; @@ -1850,7 +1860,7 @@ sub get_bandwidth_limit { my ($operation, $storage_list, $override) = @_; # called for each limit (global, per-storage) with the 'default' and the - # $operation limit and should udpate $override for every limit affecting + # $operation limit and should update $override for every limit affecting # us. my $use_global_limits = 0; my $apply_limit = sub { @@ -1929,4 +1939,16 @@ sub assert_sid_unused { return undef; } +# removes leading/trailing spaces and (back)slashes completely +# substitutes every non-ASCII-alphanumerical char with '_', except '_.-' +sub normalize_content_filename { + my ($filename) = @_; + + chomp $filename; + $filename =~ s/^.*[\/\\]//; + $filename =~ s/[^a-zA-Z0-9_.-]/_/g; + + return $filename; +} + 1;