container: Update FSID inside the image file to keep it bootable

- When fsid of the btrfs filesystem is updated, grub needs to updated too.
Otherwise, the image can't be booted into using a virtual machine.

- When fsid of the btrfs filesystem is updated, /etc/fstab needs to updated too.
Otherwise, the root file system can't be remounted as read-write.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
Sunil Mohan Adapa 2024-12-19 16:08:36 -08:00 committed by Veiko Aasa
parent 4f9e5e9e14
commit 6f6e2bc876
No known key found for this signature in database
GPG Key ID: 478539CAE680674E

View File

@ -591,7 +591,9 @@ def _resize_disk_image(image_file: pathlib.Path, new_size: str):
check=True)
last_line = process.stdout.decode().splitlines()[-1]
partition = '/dev/mapper/' + last_line.split()[2]
old_fsid = _get_fsid(partition)
subprocess.run(['sudo', 'btrfstune', '-uf', partition], check=True)
new_fsid = _get_fsid(partition)
with tempfile.TemporaryDirectory(
dir=_get_work_directory().resolve()) as mount_point:
@ -599,6 +601,7 @@ def _resize_disk_image(image_file: pathlib.Path, new_size: str):
subprocess.run(
['sudo', 'btrfs', 'filesystem', 'resize', 'max', mount_point],
check=True, stdout=subprocess.DEVNULL)
_update_fsid(mount_point, partition, old_fsid, new_fsid)
subprocess.run(['sudo', 'umount', mount_point], check=True)
subprocess.run(
@ -606,6 +609,46 @@ def _resize_disk_image(image_file: pathlib.Path, new_size: str):
stderr=subprocess.DEVNULL)
def _get_fsid(partition: str) -> str:
"""Return the btrfs filesystem ID for a device."""
# Find the new FSID
process = subprocess.run(
['sudo', 'btrfs', 'filesystem', 'show', partition], check=True,
stdout=subprocess.PIPE)
return process.stdout.decode().splitlines()[0].rpartition(' ')[2]
def _update_fsid(mount_point: str, partition: str, old_fsid: str,
new_fsid: str):
"""After changing btrfs fsid, run grub-install to keep image bootable."""
# Guess the loopback device for the filesystem
matches = re.match(r'^/dev/mapper/loop(\d+)p\d+$', partition)
assert matches
loop_device = f'/dev/loop{matches[1]}'
# Mount /dev, /proc and run grub commands in chroot
grub_args = []
if platform.machine() in ('aarch64', 'arm64'):
grub_args += ['--no-nvram']
subprocess.run(
['sudo', 'mount', '-o', 'bind', '/dev/', f'{mount_point}/dev'],
check=True)
subprocess.run(
['sudo', 'mount', '-o', 'bind', '/proc/', f'{mount_point}/proc'],
check=True)
subprocess.run(['sudo', 'chroot', mount_point, 'update-grub'], check=True)
subprocess.run(
['sudo', 'chroot', mount_point, 'grub-install', loop_device] +
grub_args, check=True)
subprocess.run([
'sudo', 'chroot', mount_point, 'sed', '-ie',
f's|UUID={old_fsid}|UUID={new_fsid}|', '/etc/fstab'
], check=True)
subprocess.run(['sudo', 'umount', f'{mount_point}/proc'], check=True)
subprocess.run(['sudo', 'umount', f'{mount_point}/dev'], check=True)
def _runc(image_file: pathlib.Path, command: list[str], **kwargs):
"""Run a command inside the container."""
subprocess.run([