diff --git a/assemblers/org.osbuild.qemu b/assemblers/org.osbuild.qemu index 97149d3b..db80e6bb 100755 --- a/assemblers/org.osbuild.qemu +++ b/assemblers/org.osbuild.qemu @@ -77,6 +77,53 @@ def mkfs_xfs(device, uuid): subprocess.run(["mkfs.xfs", "-m", f"uuid={uuid}", device], encoding='utf-8', check=True) +def create_partition_table(image, ptuuid): + """Set up the partition table of the image""" + partition_table = f"label: mbr\nlabel-id: {ptuuid}\nbootable, type=83" + subprocess.run(["sfdisk", "-q", image], input=partition_table, encoding='utf-8', check=True) + + r = subprocess.run(["sfdisk", "--json", image], stdout=subprocess.PIPE, encoding='utf-8', check=True) + partition_table = json.loads(r.stdout) + return partition_table + + +def install_grub2(image, fs_module, partition_offset): + """Install grub2 to image""" + grub2_core = "/var/tmp/grub2-core.img" + + # Create the level-2 bootloader + # The purpose of this is to find the grub modules and configuration + # to be able to start the level-3 bootloader. It contains the modules + # necessary to do this, but nothing else. + subprocess.run(["grub2-mkimage", + "--verbose", + "--directory", "/usr/lib/grub/i386-pc", + "--prefix", "(,msdos1)/boot/grub2", + "--format", "i386-pc", + "--compression", "auto", + "--output", grub2_core, + "part_msdos", fs_module, "biosdisk"], + check=True) + + assert os.path.getsize(grub2_core) < partition_offset - 512 + + with open(image, "rb+") as image_f: + # Install the level-1 bootloader into the start of the MBR + # The purpose of this is simply to jump into the level-2 bootloader. + with open("/usr/lib/grub/i386-pc/boot.img", "rb") as boot_f: + # The boot.img file is 512 bytes, but we must only copy the first 440 + # bytes, as these contain the bootstrapping code. The rest of the + # first sector contains the partition table, and must not be + # overwritten. + image_f.write(boot_f.read(440)) + + # Install the level-2 bootloader into the space after the MBR, before + # the first partition. + with open(grub2_core, "rb") as core_f: + image_f.seek(512) + shutil.copyfileobj(core_f, image_f) + + def main(tree, output_dir, options, loop_client): fmt = options["format"] filename = options["filename"] @@ -102,52 +149,18 @@ def main(tree, output_dir, options, loop_client): raise ValueError("`root_fs_type` must be either ext4 or xfs") image = "/var/tmp/osbuild-image.raw" - grub2_core = "/var/tmp/grub2-core.img" # Create an empty image file subprocess.run(["truncate", "--size", str(size), image], check=True) - # Set up the partition table of the image - partition_table = f"label: mbr\nlabel-id: {ptuuid}\nbootable, type=83" - subprocess.run(["sfdisk", "-q", image], input=partition_table, encoding='utf-8', check=True) - - r = subprocess.run(["sfdisk", "--json", image], stdout=subprocess.PIPE, encoding='utf-8', check=True) - partition_table = json.loads(r.stdout) + # The partition table + partition_table = create_partition_table(image, ptuuid) partition = partition_table["partitiontable"]["partitions"][0] partition_offset = partition["start"] * 512 partition_size = partition["size"] * 512 # Create the level-2 bootloader - # The purpose of this is to find the grub modules and configuration - # to be able to start the level-3 bootloader. It contains the modules - # necessary to do this, but nothing else. - subprocess.run(["grub2-mkimage", - "--verbose", - "--directory", "/usr/lib/grub/i386-pc", - "--prefix", "(,msdos1)/boot/grub2", - "--format", "i386-pc", - "--compression", "auto", - "--output", grub2_core, - "part_msdos", grub2_fs_module, "biosdisk"], - check=True) - - assert os.path.getsize(grub2_core) < partition_offset - 512 - - with open(image, "rb+") as image_f: - # Install the level-1 bootloader into the start of the MBR - # The purpose of this is simply to jump into the level-2 bootloader. - with open("/usr/lib/grub/i386-pc/boot.img", "rb") as boot_f: - # The boot.img file is 512 bytes, but we must only copy the first 440 - # bytes, as these contain the bootstrapping code. The rest of the - # first sector contains the partition table, and must not be - # overwritten. - image_f.write(boot_f.read(440)) - - # Install the level-2 bootloader into the space after the MBR, before - # the first partition. - with open(grub2_core, "rb") as core_f: - image_f.seek(512) - shutil.copyfileobj(core_f, image_f) + install_grub2(image, grub2_fs_module, partition_offset) with loop_client.device(image, partition_offset, partition_size) as loop: # Populate the first partition of the image with a filesystem