debian-forge/assemblers/io.weldr.qcow2
Tom Gundersen d9488e5f8b losetup: make sure image sizes are multiples of 512K sectors
Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-06-26 12:57:20 +02:00

82 lines
3 KiB
Python
Executable file

#!/usr/bin/python3
import contextlib
import json
import math
import os
import shutil
import subprocess
import sys
import tempfile
def tree_size(tree):
size = 0
for root, dirs, files in os.walk(tree):
for entry in files + dirs:
path = os.path.join(root, entry)
size += os.stat(path, follow_symlinks=False).st_size
return size
@contextlib.contextmanager
def mount(source, dest, *options):
os.makedirs(dest, 0o755, True)
subprocess.run(["mount", *options, source, dest], check=True)
try:
yield
finally:
subprocess.run(["umount", "-R", dest], check=True)
@contextlib.contextmanager
def mount_api(dest):
with mount("/dev", f"{dest}/dev", "-o", "rbind"), \
mount("/proc", f"{dest}/proc", "-o", "rbind"), \
mount("/sys", f"{dest}/sys", "-o", "rbind"), \
mount("none", f"{dest}/run", "-t", "tmpfs"):
yield
@contextlib.contextmanager
def loop_device(image):
r = subprocess.run(["losetup", "--partscan", "--show", "--find", image], stdout=subprocess.PIPE, encoding="utf-8", check=True)
loop = r.stdout.strip()
try:
yield loop
finally:
subprocess.run(["losetup", "-d", loop], check=True)
def main(tree, input_dir, output_dir, options):
filename = options["filename"]
partition_table_id = options["partition_table_id"]
root_fs_uuid = options["root_fs_uuid"]
# Create a working directory on a tmpfs, maybe we should implicitly
# always do this.
with tempfile.TemporaryDirectory() as workdir:
image = f"{workdir}/image.raw"
mountpoint = f"{workdir}/mnt"
# Create an empty image file of the right size
size = int(math.ceil(tree_size(tree) * 1.2 / 512) * 512)
subprocess.run(["truncate", "--size", str(size), image], check=True)
# Set up the partition table of the image
partition_table = "label: mbr\nlabel-id: {partition_table_id}\nbootable, type=83"
subprocess.run(["sfdisk", "-q", image], input=partition_table, encoding='utf-8', check=True)
# Mount the created image as a loopback device
with loop_device(image) as loop:
# Populate the first partition of the image with an ext4 fs and fill it with the contents of the
# tree we are operating on.
subprocess.run(["mkfs.ext4", "-d", tree, "-U", root_fs_uuid, f"{loop}p1"], check=True)
# Install grub2 into the boot sector of the image
# It is unclear why chrooting into the image is required.
with mount(f"{loop}p1", mountpoint):
with mount_api(mountpoint):
subprocess.run(["chroot", mountpoint, "grub2-install", "--no-floppy", "--target", "i386-pc", loop], check=True)
subprocess.run(["qemu-img", "convert", "-O" "qcow2", "-c", image, f"{output_dir}/{filename}"], check=True)
if __name__ == '__main__':
args = json.load(sys.stdin)
r = main(args["tree"], args["input_dir"], args["output_dir"], args["options"])
sys.exit(r)