From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 232D71FF137 for ; Tue, 31 Mar 2026 13:50:44 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 5E9AD1A781; Tue, 31 Mar 2026 13:50:52 +0200 (CEST) From: Fiona Ebner To: pve-devel@lists.proxmox.com Subject: [PATCH storage v2 6/7] fix #7143: ensure qcow2 volume (de)activation is limited to target volume chain Date: Tue, 31 Mar 2026 13:49:54 +0200 Message-ID: <20260331115011.102276-7-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260331115011.102276-1-f.ebner@proxmox.com> References: <20260331115011.102276-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1774957758717 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.494 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: ONFWEF6UTTW6Y4IJ6WQ5PQRSYSINPXYA X-Message-ID-Hash: ONFWEF6UTTW6Y4IJ6WQ5PQRSYSINPXYA X-MailFrom: f.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: When using tag-based (de)activation for qcow2 volumes, volumes with the same name (or snapshot volumes based on a volume with the same name) on other storages are affected too. Including the storage ID in the tag would be a possibility, but would mean updating the tags after moving the volume and would break moving volumes or renaming outside the Proxmox VE stack and would be rather involved to do for already existing images. Use a regex with '--select' instead of using tags to select the volumes in the qcow2 volume chain. Signed-off-by: Fiona Ebner --- New in v2. src/PVE/Storage/LVMPlugin.pm | 66 ++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/PVE/Storage/LVMPlugin.pm b/src/PVE/Storage/LVMPlugin.pm index 79a52c5..b4cb0c5 100644 --- a/src/PVE/Storage/LVMPlugin.pm +++ b/src/PVE/Storage/LVMPlugin.pm @@ -713,7 +713,12 @@ my sub alloc_lvm_image { die "not enough free space ($free < $size)\n" if $free < $size; my $tags = ["pve-vm-$vmid"]; - #tags all snapshots volumes with the main volume tag for easier activation of the whole group + # TODO PVE 10 - stop setting the volume name as a tag? It was initially used for (de)activation, + # but there are false positives if there are multiple storages, see bug #7143. Including the + # storage ID as a fix would've made moving disks more involved and break manual moving or + # renaming. Instead (de)activation switched to using '--select'. The rename_volume() method + # needs to be adapted as well. + # tags all snapshots volumes with the main volume tag push @$tags, "\@pve-$name" if $fmt eq 'qcow2'; lvcreate($vg, $name, $lvmsize, $tags); @@ -952,21 +957,53 @@ sub deactivate_storage { run_command($cmd, errmsg => "can't deactivate VG '$scfg->{vgname}'"); } +=head3 get_activate_volume_target_opts() + + my ($target_opts, $target_str) = + get_activate_volume_target_opts($class, $scfg, $path, $volname); + +Returns C<$target_opts>, which is an array reference with parameters for C for selecting +the volume given by C<($path,$volname)>, or volume chain for C. Also returns a description of +the target C<$target_str> that can be used for log or error messages. + +=cut + +my sub get_activate_volume_target_opts { + my ($class, $scfg, $path, $volname) = @_; + + my ($name, $format) = ($class->parse_volname($volname))[1,6]; + + my ($target_opts, $target_str); + + if ($format eq 'qcow2') { + my $vg = $scfg->{vgname}; + $name =~ s/\.qcow2$//; + + my $filter = "vg_name = $vg"; + $filter .= "&& lv_name =~ '^(${name}|snap_${name}_.+).qcow2\$'"; + + $target_opts = ['--select', $filter]; + $target_str = "volume chain for LV '$path'"; + } else { + $target_opts = [$path]; + $target_str = "LV '$path'"; + } + + return ($target_opts, $target_str); +} + sub activate_volume { my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_; my $path = $class->path($scfg, $volname, $storeid, $snapname); - #activate volume && all snapshots volumes by tag - my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) = - $class->parse_volname($volname); + my ($target_opts, $target_str) = + get_activate_volume_target_opts($class, $scfg, $path, $volname); - $path = "\@pve-$name" if $format eq 'qcow2'; - - my $cmd = ['/sbin/lvchange', '-aey', $path]; - run_command($cmd, errmsg => "can't activate LV '$path'"); - $cmd = ['/sbin/lvchange', '--refresh', $path]; - run_command($cmd, errmsg => "can't refresh LV '$path' for activation"); + my $cmd = ['/sbin/lvchange', '-aey', $target_opts->@*]; + run_command($cmd, errmsg => "can't activate $target_str"); + $cmd = ['/sbin/lvchange', '--refresh', $target_opts->@*]; + run_command($cmd, errmsg => "can't refresh $target_str for activation"); } sub deactivate_volume { @@ -975,12 +1012,11 @@ sub deactivate_volume { my $path = $class->path($scfg, $volname, $storeid, $snapname); return if !-b $path; - my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) = - $class->parse_volname($volname); - $path = "\@pve-$name" if $format eq 'qcow2'; + my ($target_opts, $target_str) = + get_activate_volume_target_opts($class, $scfg, $path, $volname); - my $cmd = ['/sbin/lvchange', '-aln', $path]; - run_command($cmd, errmsg => "can't deactivate LV '$path'"); + my $cmd = ['/sbin/lvchange', '-aln', $target_opts->@*]; + run_command($cmd, errmsg => "can't deactivate $target_str"); } sub volume_resize { -- 2.47.3