From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 6C90A1FF0AD for ; Sun, 04 Oct 2026 19:05:38 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id A1011214F4; Sun, 04 Oct 2026 19:05:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dualfroz.com; s=dkim; t=1791133528; h=from:subject:date:message-id:to:mime-version: content-transfer-encoding; bh=JGIh3SGfbGF+ZjkPO+mX3MO09qncmjCslm8Qy8T4J88=; b=KXV7hWsj/IqVb9mIROr7r6wsyw3Kvp3rUp+qGFF9JXNUuX86BDFpGhkKK0aBOTUyxHFCh3 FNxYlvxY5Qs+0UEm1Xhmw2i5XFyq0FBv1PYgEBWQN4W57VNynwxCyJm5835pj8RT8vaGCo PCCdlpHyR61bAKGdmiVu5nmB4QYqhgEU87hklaPRM4JPeipnM9knQy1YnF0UTWdHM0cIV7 1SrvwfVbkk10hkEsgZorMOV6JaPgfEDIP/Jk8NOZsajVnU3vqvJLW8uDCJEGMRuT2QS6eA szj7hztriOgrnBka6L481u/xrJUnPPODYeurUZEsoBuaGF7t/EwABzDFjMeVJw== From: Michal Fox To: pve-devel@lists.proxmox.com Subject: [PATCH manager] fix #7178: pve8to9: detect qcow2 TPM state volumes containing raw data Date: Sun, 4 Oct 2026 17:05:18 +0000 Message-ID: <20261004170518.7-1-me@dualfroz.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Last-TLS-Session-Version: TLSv1.3 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.165 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy 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: BFZ3PLM2SORLQMRFQY2MXLBTRPTQV7F6 X-Message-ID-Hash: BFZ3PLM2SORLQMRFQY2MXLBTRPTQV7F6 X-MailFrom: me@dualfroz.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: Before qemu-server 8.3.3, cloning a VM template with a raw TPM state on a directory storage as linked clone created a qcow2 overlay for it. swtpm_setup used that file as raw TPM state and, as it found no valid state in it, overwrote it with a new one, turning it into a raw file with a qcow2 name. With Proxmox VE 9, the TPM state is attached with the format of the volume name, so such a VM fails to start with 'Image is not in qcow2 format'. Since qemu-server 8.3.3, it also fails to start on Proxmox VE 8, as swtpm only accepts raw volumes there, but the VM might not have been started since then. Check the TPM state volumes of all VMs that are named as qcow2 and on a file based storage for the qcow2 magic, and warn about those that do not have it, with how to fix them, as described in the bug report. Signed-off-by: Michal Fox --- Tested the new check with mocked VM configs and storage config, on a directory storage with a linked clone TPM state overwritten with raw data (which 'qemu-img info' reports as raw), a real qcow2 and a raw TPM state, one on LVM, one with a missing file and one on a storage that does not exist: only the first one is reported, the last two are skipped with the error. With the detection disabled or always true, the test fails. This is for master, it also applies to stable-8 with a 3-way merge, as only the context of the 'use' lines differs there. PVE/CLI/pve8to9.pm | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/PVE/CLI/pve8to9.pm b/PVE/CLI/pve8to9.pm index 7305a7b6..b147f56e 100644 --- a/PVE/CLI/pve8to9.pm +++ b/PVE/CLI/pve8to9.pm @@ -27,6 +27,7 @@ use PVE::Storage::Plugin; use PVE::Tools qw(run_command split_list file_get_contents trim); use PVE::QemuConfig; use PVE::QemuServer; +use PVE::QemuServer::Drive; use PVE::QemuServer::Machine; use PVE::QemuServer::Network; use PVE::VZDump::Common; @@ -1076,6 +1077,51 @@ my sub check_qemu_machine_versions { } } +sub check_qemu_tpmstate_volumes { + log_info("Checking VM TPM state volumes for qcow2 files containing raw data.."); + + my $storecfg = PVE::Storage::config(); + my $affected = []; + + my $vms = PVE::QemuServer::config_list(); + for my $vmid (sort { $a <=> $b } keys $vms->%*) { + my $conf = PVE::QemuConfig->load_config($vmid); + next if !$conf->{tpmstate0}; + + my $volid = PVE::QemuServer::Drive::parse_drive('tpmstate0', $conf->{tpmstate0})->{file}; + eval { + my ($storeid) = PVE::Storage::parse_volume_id($volid); + my $format = (PVE::Storage::parse_volname($storecfg, $volid))[6]; + my $scfg = PVE::Storage::storage_config($storecfg, $storeid); + # only volumes on file based storages could end up like this + return if $format ne 'qcow2' || !$scfg->{path}; + + my $path = PVE::Storage::path($storecfg, $volid); + open(my $fh, '<', $path) or die "unable to open '$path' - $!\n"; + my $magic = ''; + read($fh, $magic, 4); + close($fh); + + # before qemu-server 8.3.3, swtpm_setup used the qcow2 overlay of linked clones as raw + # file and overwrote it with a new state + push $affected->@*, "VM $vmid: $volid" if $magic ne "QFI\xfb"; + }; + log_skip("could not check TPM state volume '$volid' of VM $vmid - $@") if $@; + } + + if (!scalar($affected->@*)) { + log_pass("No qcow2 TPM state volumes with raw data found."); + return; + } + + log_warn( + "The TPM state volumes of the following VMs are qcow2 files that contain raw data, which" + . " happened with linked clones. Such a VM fails to start in Proxmox VE 9. Rename the" + . " file to end with '.raw' and adapt the 'tpmstate0' line of the VM config accordingly," + . " also dropping the base volume, like '101/base-101-disk-2.raw/':\n\t" + . join("\n\t", $affected->@*)); +} + my sub check_max_length { my ($raw, $max_length, $warning) = @_; log_warn($warning) if defined($raw) && length($raw) > $max_length; @@ -2098,6 +2144,8 @@ sub check_virtual_guests { } check_qemu_machine_versions(); + + check_qemu_tpmstate_volumes(); } my $LEGACY_IPAM_DB = "/etc/pve/priv/ipam.db"; -- 2.43.0