use Net::IP;
use POSIX;
+use PVE::ProcFSTools;
use PVE::RPCEnvironment;
use PVE::Storage::Plugin;
use PVE::Tools qw(run_command);
} else {
$scfg->{mountpoint} = $mountpoint;
}
+
+ return;
}
sub path {
sub volume_snapshot_rollback {
my ($class, $scfg, $storeid, $volname, $snap) = @_;
- my $vname = ($class->parse_volname($volname))[1];
+ my (undef, $vname, undef, undef, undef, undef, $format) = $class->parse_volname($volname);
+
+ my $msg = $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$vname\@$snap");
+
+ # we have to unmount rollbacked subvols, to invalidate wrong kernel
+ # caches, they get mounted in activate volume again
+ # see zfs bug #10931 https://github.com/openzfs/zfs/issues/10931
+ if ($format eq 'subvol') {
+ $class->zfs_request($scfg, undef, 'unmount', "$scfg->{pool}/$vname");
+ }
- $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$vname\@$snap");
+ return $msg;
}
sub volume_rollback_is_possible {
my ($class, $storeid, $scfg, $cache) = @_;
# Note: $scfg->{pool} can include dataset <pool>/<dataset>
- my $pool = $scfg->{pool};
- $pool =~ s!/.*$!!;
+ my $dataset = $scfg->{pool};
+ my $pool = ($dataset =~ s!/.*$!!r);
+
+ my $dataset_mounted = sub {
+ my $mounts = eval { PVE::ProcFSTools::parse_proc_mounts() };
+ warn "$@\n" if $@;
+ foreach my $mp (@$mounts) {
+ my ($what, $dir, $fs) = @$mp;
+ next if $fs ne 'zfs';
+ # check for root-dataset of storage or any child-dataset.
+ # root-dataset could have 'canmount=off'. If any child is mounted
+ # heuristically assume that `zfs mount -a` was successful
+ next if $what !~ m!^$dataset(?:/|$)!;
+ return 1;
+ }
+ return 0;
+ };
my $pool_imported = sub {
- my @param = ('-o', 'name', '-H', "$pool");
+ my @param = ('-o', 'name', '-H', $pool);
my $res = eval { $class->zfs_request($scfg, undef, 'zpool_list', @param) };
- if ($@) {
- warn "$@\n";
- return undef;
- }
+ warn "$@\n" if $@;
+
return defined($res) && $res =~ m/$pool/;
};
- if (!$pool_imported->()) {
- # import can only be done if not yet imported!
- my @param = ('-d', '/dev/disk/by-id/', '-o', 'cachefile=none', "$pool");
- eval { $class->zfs_request($scfg, undef, 'zpool_import', @param) };
- if (my $err = $@) {
- # just could've raced with another import, so recheck if it is imported
- die "could not activate storage '$storeid', $@\n" if !$pool_imported->();
+ if (!$dataset_mounted->()) {
+ if (!$pool_imported->()) {
+ # import can only be done if not yet imported!
+ my @param = ('-d', '/dev/disk/by-id/', '-o', 'cachefile=none', $pool);
+ eval { $class->zfs_request($scfg, undef, 'zpool_import', @param) };
+ if (my $err = $@) {
+ # just could've raced with another import, so recheck if it is imported
+ die "could not activate storage '$storeid', $err\n" if !$pool_imported->();
+ }
}
+ eval { $class->zfs_request($scfg, undef, 'mount', '-a') };
+ die "could not activate storage '$storeid', $@\n" if $@;
}
return 1;
}
return 1 if defined($snapname);
- my (undef, undef, undef, undef, undef, undef, $format) = $class->parse_volname($volname);
+ my (undef, $dataset, undef, undef, undef, undef, $format) = $class->parse_volname($volname);
if ($format eq 'raw') {
$class->zfs_wait_for_zvol_link($scfg, $volname);
} elsif ($format eq 'subvol') {
- my $mounted = $class->zfs_get_properties($scfg, 'mounted', "$scfg->{pool}/$volname");
+ my $mounted = $class->zfs_get_properties($scfg, 'mounted', "$scfg->{pool}/$dataset");
if ($mounted !~ m/^yes$/) {
- $class->zfs_request($scfg, undef, 'mount', "$scfg->{pool}/$volname");
+ $class->zfs_request($scfg, undef, 'mount', "$scfg->{pool}/$dataset");
}
}
my $zfspath = "$scfg->{pool}/$dataset";
my $suffix = defined($base_snapshot) ? "\@$base_snapshot" : '';
my $exists = 0 == run_command(['zfs', 'get', '-H', 'name', $zfspath.$suffix],
- noerr => 1, errfunc => sub {});
+ noerr => 1, quiet => 1);
if (defined($base_snapshot)) {
die "base snapshot '$zfspath\@$base_snapshot' doesn't exist\n" if !$exists;
} elsif ($exists) {