From 6dafa36fc7d13ef6b9417d4ee260425692eebf25 Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Fri, 2 Sep 2022 13:51:46 +0200 Subject: [PATCH] image-info: remove base loop device before analysing partitions Instead of keeping the loop device of the base image and then opening each partition as a loop device, remove the original loop device of the base image and then create a loop device for each partition from the file itself using the partition offsets. The open_image() function is renamed to convert_image() and now only handles converting qcow2 files to raw files if necessary. The loop_open() context is done in analyse_image() instead, so that the base loop device can be closed without removing the converted image. This fixes the following issue with LVM partitions: When the same lvm partition UUID is on two devices (e.g., /dev/loop0p4 and /dev/loop1), the 'vgchange -ay' command fails with the following error: Cannot activate LVs in VG rootvg while PVs appear on duplicate devices. This happens when we open the LVM partition as a separate loop device, which we do for all partitions that we want to inspect. NB: It's possible to restrict the vgchange command to a specific device with --devices, but this isn't available in older versions of lvm2 (it was introduced in 2.03.11). --- tools/image-info | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tools/image-info b/tools/image-info index ecf10bb85..376ac53ef 100755 --- a/tools/image-info +++ b/tools/image-info @@ -70,7 +70,7 @@ def loop_open(ctl, image, *, offset=None, size=None): @contextlib.contextmanager -def open_image(ctl, image, fmt): +def convert_image(ctl, image, fmt): with tempfile.TemporaryDirectory(dir="/var/tmp") as tmp: if fmt["type"] != "raw": target = os.path.join(tmp, "image.raw") @@ -93,10 +93,7 @@ def open_image(ctl, image, fmt): else: target = image - size = os.stat(target).st_size - - with loop_open(ctl, target, offset=0, size=size) as dev: - yield target, dev + yield target @contextlib.contextmanager @@ -2556,7 +2553,7 @@ def partition_is_lvm(part: Dict) -> bool: return part["type"].upper() in ["E6D6D379-F507-44C2-A23C-238F2A3DF928", "8E"] -def append_partitions(report, device, loctl): +def append_partitions(report, image, loctl): partitions = report["partitions"] with contextlib.ExitStack() as cm: @@ -2564,7 +2561,7 @@ def append_partitions(report, device, loctl): filesystems = {} for part in partitions: start, size = part["start"], part["size"] - dev = cm.enter_context(loop_open(loctl, device, offset=start, size=size)) + dev = cm.enter_context(loop_open(loctl, image, offset=start, size=size)) read_partition(dev, part) if partition_is_lvm(part): lvm = cm.enter_context(discover_lvm(dev)) @@ -2630,17 +2627,20 @@ def analyse_image(image): imgfmt = read_image_format(image) report = {"image-format": imgfmt} - with open_image(loctl, image, imgfmt) as (_, device): - report["bootloader"] = read_bootloader_type(device) - report.update(read_partition_table(device)) + with convert_image(loctl, image, imgfmt) as target: + size = os.stat(target).st_size + with loop_open(loctl, target, offset=0, size=size) as device: + report["bootloader"] = read_bootloader_type(device) + report.update(read_partition_table(device)) + if not report["partition-table"]: + # no partition table: mount device and treat it as a partition + with mount(device) as tree: + append_filesystem(report, tree) + return report - if report["partition-table"]: - append_partitions(report, device, loctl) - else: - with mount(device) as tree: - append_filesystem(report, tree) - - return report + # close loop device and descend into partitions on image file + append_partitions(report, target, loctl) + return report def append_directory(report, tree):