Skip to content

NAS-143377 / 25.10.6 / Move both nvram and TPM state when a VM is renamed - #19648

Closed
eschultz wants to merge 1 commit into
truenas:stable/goldeyefrom
eschultz:fix/NAS-143377-vm-rename-state
Closed

eschultz wants to merge 1 commit into
truenas:stable/goldeyefrom
eschultz:fix/NAS-143377-vm-rename-state

Conversation

@eschultz

@eschultz eschultz commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

NAS-143377 — against stable/goldeye.

Renaming a VM on 25.10 does two wrong things:

  1. A UEFI VM that has never been started reports an error for a rename that succeeded.
  2. A VM with a TPM silently loses its TPM state — the rename moves the nvram file but not the TPM state directory, and the re-defined domain points swtpm at a directory that does not exist.

Both are already fixed on 26.0 and master by NAS-140717 (8945d05571, "Move VM NVRAM and TPM atomically with VM rename"). This backports that decision.

The error on a rename that worked

do_update raises whenever the nvram file is missing and the bootloader is UEFI on both sides. A UEFI VM that has never booted has no nvram file — libvirt creates it from the <nvram template=...> attribute at first start — so "never booted" and "nvram was lost" are indistinguishable and the code treats both as the second, after datastore.update has committed. On 25.10.5:

[EFAULT] VM name has been updated but nvram file for probe does not exist which can
result in probe_renamed VM not booting properly.

…while vm.query returns probe_renamed.

The TPM state left behind

get_vm_tpm_state_dir_name builds {id}_{name}_tpm_state exactly the way the nvram file is built, and has one consumer — the swtpm backend in supervisor/domain_xml.py. Nothing renames it. Measured on 25.10.5 with a TPM VM booted once, then stopped and renamed:

### before rename, XML tpm path:
<source type='dir' path='/var/db/system/vm/tpm/61_probe_tpm_state'/>
### rename returned success
### after rename, XML tpm path:
<source type='dir' path='/var/db/system/vm/tpm/61_probe_renamed_tpm_state'/>
### on disk:
/var/db/system/vm/tpm/61_probe_tpm_state

swtpm initialises blank state on the next boot. For a Windows guest the TPM looks factory reset: BitLocker asks for the recovery key and anything sealed to the TPM is gone.

Fix

A _rename_vm_state helper that moves both artefacts and logs when there was no nvram to move. Missing sources are skipped rather than raised — NAS-140717's wording: "Missing sources are silently skipped, a VM that has never booted has no on-disk state and libvirt/swtpm will initialise both on first start." The warning is kept because 26.0 keeps one too.

Verified on 25.10.5

  • a never-booted UEFI VM renames with no error, new name returned
  • middlewared.log gets Renamed VM 'f7a' to 'f7a_renamed' with no nvram file to move; libvirt will create one on next boot.
  • a UEFI_CSM VM renames with no warning — it legitimately has no nvram
  • a booted UEFI + TPM VM renames and both move: 63_f7b_VARS.fd → 63_f7b_renamed_VARS.fd, 63_f7b_tpm_state → 63_f7b_renamed_tpm_state
  • the re-defined domain XML then addresses both new paths

Found while testing, not patched

vm.delete does not remove the TPM state directory either — undefine_domain passes VIR_DOMAIN_UNDEFINE_NVRAM so libvirt reaps the nvram file, but VIR_DOMAIN_UNDEFINE_TPM is never passed and nothing unlinks the directory. Not theoretical: the 25.10.5 box I tested on carries three orphans from VMs that no longer exist (7_, 8_, 9_Windows_11_Pro_tpm_state; ids 7/8/9 are not in vm.query). NAS-140717 fixed this in the same commit with delete_vm_state() — happy to fold it in.

Scope

Keeps 25.10's plain os.rename; truenas_os/renameat2/openat2 do not exist on this branch, so AT_RENAME_NOREPLACE and RESOLVE_NO_SYMLINKS can't come across. Stale-destination reach is low — vm_vm uses sqlite_autoincrement, so ids are not reused.

Two further things NAS-140717 changed that this does not: it moved the filesystem work ahead of datastore.update with rollback, and added unit tests for the helper. The ordering is the deeper fix and anything other than FileNotFoundError still surfaces after the commit here — I kept this to the two user-visible failures rather than restructuring do_update on a stable branch, but say the word.

…amed

Renaming a VM did two wrong things. A UEFI VM that had never been started
raised CallError for a rename that had already been committed, because libvirt
only creates the nvram file at first boot, so 'never booted' and 'nvram lost'
look identical. And a VM with a TPM kept its TPM state directory under the old
name while the re-defined domain pointed swtpm at the new one, so swtpm
initialised blank state on the next boot and the guest saw a factory-reset TPM.

Replace the try/except with a helper that moves the nvram file and the TPM
state directory together, skipping either if it is absent. Log a warning when a
UEFI VM had no nvram to move: that is normal for a VM that has never booted,
but it also covers the case where one was lost, and such a VM comes up with a
fresh nvram and no enrolled Secure Boot keys.

Both halves are already fixed on 26.0 and master by NAS-140717 (8945d05).
This keeps 25.10's plain os.rename, since truenas_os and renameat2 do not
exist on this branch.
@yocalebo
yocalebo requested a review from Qubad786 September 14, 2026 19:40
@yocalebo

Copy link
Copy Markdown
Contributor

This has been fixed in 26/27 and we have no plans to fix this in 25.10 train at this time. Going to close it out.

@yocalebo yocalebo closed this Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants