LinboImageManager.restore moves every file out of the base image directory into the new backup, then moves the chosen backup's files back. Backups only ever contain the image-scoped sidecars (<image>.qcow2.desc, .info, .vdi), so the common ones — <name>.reg, <name>.postsync, <name>.prestart — are archived and never restored. After a rollback the image no longer has them.
Reproduced on 7.4 with linuxmusterTools@lmn74, in a scratch tree:
before: win11.qcow2 win11.qcow2.desc win11.qcow2.info win11.reg win11.postsync
backups/202601271107/{win11.qcow2, win11.qcow2.desc, win11.qcow2.info}
after restore("win11", "27/01/2026 11:07"):
win11.qcow2 win11.qcow2.desc win11.qcow2.info
backups/202608111043/{win11.qcow2, win11.qcow2.desc, win11.qcow2.info, win11.reg, win11.postsync}
The registry patch and the postsync script are gone from the image; they exist only inside the new backup directory.
images.py already separates the two groups for exactly this reason — EXTRA_IMAGE_FILES is keyed on the image filename and EXTRA_COMMON_FILES on the image name, and delete_files and rename both treat them differently. restore is the one place that does not: it iterates the directory rather than the two lists.
Is that intentional? I can see an argument for archiving the whole directory as it stood. But the asymmetry with rename, which carries the common files across, makes it look unintended, and the practical effect is that a rollback silently loses configuration that was not part of what the user rolled back.
If it is a bug, the fix looks small — leave the EXTRA_COMMON_FILES in place instead of moving them, or move them back after the backup's files land. I did not want to send a patch for a behaviour that might be deliberate, so: which way would you like it?
Context: this surfaced while routing the image management operations in the API, linuxmuster/linuxmuster-api#33. That PR exposes restore as-is and documents this, since webui7 reaches the same code path today and routing it changes nothing for the worse.
Two smaller things found alongside, mentioned here only so they are written down somewhere:
- Two restores of the same image within one minute collide: the new backup directory is
datetime.now().strftime('%Y%m%d%H%M'), so the second raises ImageExistsError. Nothing is lost, and a caller can retry.
NAME_RULES['linbo_image'] accepts a leading dot, so an image can be renamed to .incoming and collide with the upload staging directory used by image_sync. Only a global-administrator can trigger it.
LinboImageManager.restoremoves every file out of the base image directory into the new backup, then moves the chosen backup's files back. Backups only ever contain the image-scoped sidecars (<image>.qcow2.desc,.info,.vdi), so the common ones —<name>.reg,<name>.postsync,<name>.prestart— are archived and never restored. After a rollback the image no longer has them.Reproduced on 7.4 with
linuxmusterTools@lmn74, in a scratch tree:The registry patch and the postsync script are gone from the image; they exist only inside the new backup directory.
images.pyalready separates the two groups for exactly this reason —EXTRA_IMAGE_FILESis keyed on the image filename andEXTRA_COMMON_FILESon the image name, anddelete_filesandrenameboth treat them differently.restoreis the one place that does not: it iterates the directory rather than the two lists.Is that intentional? I can see an argument for archiving the whole directory as it stood. But the asymmetry with
rename, which carries the common files across, makes it look unintended, and the practical effect is that a rollback silently loses configuration that was not part of what the user rolled back.If it is a bug, the fix looks small — leave the
EXTRA_COMMON_FILESin place instead of moving them, or move them back after the backup's files land. I did not want to send a patch for a behaviour that might be deliberate, so: which way would you like it?Context: this surfaced while routing the image management operations in the API, linuxmuster/linuxmuster-api#33. That PR exposes
restoreas-is and documents this, since webui7 reaches the same code path today and routing it changes nothing for the worse.Two smaller things found alongside, mentioned here only so they are written down somewhere:
datetime.now().strftime('%Y%m%d%H%M'), so the second raisesImageExistsError. Nothing is lost, and a caller can retry.NAME_RULES['linbo_image']accepts a leading dot, so an image can be renamed to.incomingand collide with the upload staging directory used byimage_sync. Only a global-administrator can trigger it.