diff --git a/mmdebstrap-autopkgtest-build-qemu b/mmdebstrap-autopkgtest-build-qemu new file mode 100644 index 0000000..15bb02d --- /dev/null +++ b/mmdebstrap-autopkgtest-build-qemu @@ -0,0 +1,277 @@ +#!/bin/sh +# Copyright 2023 Johannes Schauer Marin Rodrigues +# Copyright 2023 Helmut Grohne +# SPDX-License-Identifier: MIT +# +# This script is mostly compatible with autopkgtest-build-qemu as shipped in +# autopkgtest. Main differences: +# * It does not support any value for --boot but efi. +# * It uses different tools, most importantly swaps out vmdb2. +# * It can be run as non-root via user namespaces. + +# We generally use single quotes to avoid variable expansion: +# shellcheck disable=SC2016 + +set -eu + +die() { + echo "$*" 1>&2 + exit 1 +} +usage() { + die "usage: $0 [--boot=|--architecture=|--apt-proxy=|--keyring=|--mirror=|--script=|--size=] [MIRROR] [ARCHITECTURE] [SCRIPT] [SIZE]" +} +usage_error() { + echo "error: $*" 1>&2 + usage +} + +BOOT=auto +ARCHITECTURE=$(dpkg --print-architecture) +IMAGE= +MIRROR= +KEYRING= +RELEASE= +SIZE=25G +SCRIPT= + +# consumed by setup-testbed +export AUTOPKGTEST_BUILD_QEMU=1 + +opt_boot() { + BOOT="$1" +} +opt_architecture() { + ARCHITECTURE="$1" +} +opt_apt_proxy() { + # consumed by setup-testbed + export AUTOPKGTEST_APT_PROXY="$1" + # consumed by mmdebstrap + if test "$1" = DIRECT; then + unset http_proxy + else + export http_proxy="$1" + fi +} +opt_keyring() { + KEYRING="$1" +} +opt_mirror() { + # consumed by setup-testbed + export MIRROR="$1" +} +opt_script() { + SCRIPT="$1" +} +opt_size() { + SIZE="$1" +} + +positional=1 +positional_1() { + # consumed by setup-testbed + export RELEASE="$1" +} +positional_2() { + IMAGE="$1" +} +positional_3() { opt_mirror "$@"; } +positional_4() { opt_architecture "$@"; } +positional_5() { opt_script "$@"; } +positional_6() { opt_size "$@"; } +positional_7() { + die "too many positional options" +} + +while test "$#" -gt 0; do + case "$1" in + --architecture=*|--boot=*|--keyring=*|--mirror=*|--script=*|--size=*) + optname="${1%%=*}" + "opt_${optname#--}" "${1#*=}" + ;; + --apt-proxy=*) + opt_apt_proxy "${1#*=}" + ;; + --architecture|--boot|--keyring|--mirror|--script|--size) + test "$#" -ge 2 || usage_error "missing argument for $1" + "opt_${1#--}" "$2" + shift + ;; + --apt-proxy) + test "$#" -ge 2 || usage_error "missing argument for $1" + opt_apt_proxy "$2" + shift + ;; + --efi) + opt_boot efi + ;; + --*) + usage_error "unrecognized argument $1" + ;; + *) + "positional_$positional" "$1" + positional=$((positional + 1)) + ;; + esac + shift +done + +test -z "$RELEASE" -o -z "$IMAGE" && usage_error "missing positional arguments" +test "$BOOT" = efi || + die "this tool does not support boot modes other than efi" + +case "$ARCHITECTURE" in + amd64) + EFIIMG=bootx64.efi + ;; + arm64) + EFIIMG=bootaa64.efi + ;; + armhf) + EFIIMG=bootarm.efi + ;; + i386) + EFIIMG=bootia32.efi + ;; + riscv64) + EFIIMG=bootriscv64.efi + ;; + *) + die "unsupported architecture" + ;; +esac + +GNU_ARCHITECTURE="$(dpkg-architecture "-a$ARCHITECTURE" -qDEB_HOST_GNU_TYPE)" +for pkg in autopkgtest "binutils-$(echo "$GNU_ARCHITECTURE" | tr _ -)" dosfstools e2fsprogs fdisk mount mtools passwd "systemd-boot-efi:$ARCHITECTURE" uidmap; do + test "$(dpkg-query -f '${db:Status-Status}' -W "$pkg")" = installed || + die "please install $pkg" +done + +BOOTSTUB="/usr/lib/systemd/boot/efi/linux${EFIIMG#boot}.stub" + +WORKDIR= + +cleanup() { + test -n "$WORKDIR" && rm -Rf "$WORKDIR" +} + +trap cleanup EXIT INT TERM QUIT + +WORKDIR=$(mktemp -d) + +FAT_OFFSET_SECTORS=$((1024*2)) +FAT_SIZE_SECTORS=$((1024*254)) + +# Make the image writeable to the first subgid. mmdebstrap will map this gid to +# the root group. unshare instead will map the current gid to 0 and the first +# subgid to 1. Therefore mmdebstrap will be able to write to the image. +rm -f "$IMAGE" +: >"$IMAGE" +unshare -U -r --map-groups=auto chown 0:1 "$IMAGE" +chmod 0660 "$IMAGE" + +set -- \ + --mode=unshare \ + --variant=important \ + --architecture="$ARCHITECTURE" + +test "$RELEASE" = jessie && + set -- "$@" --hook-dir=/usr/share/mmdebstrap/hooks/jessie-or-older + +set -- "$@" \ + "--include=init,linux-image-$ARCHITECTURE,python3" \ + '--customize-hook=echo autopkgtestvm >"$1/etc/hostname"' \ + '--customize-hook=echo 127.0.0.1 localhost autopkgtestvm >"$1/etc/hosts"' \ + '--customize-hook=passwd --root "$1" --delete root' \ + '--customize-hook=useradd --root "$1" --home-dir /home/user --create-home user' \ + '--customize-hook=passwd --root "$1" --delete user' \ + '--customize-hook=/usr/share/autopkgtest/setup-commands/setup-testbed' + +test -n "$SCRIPT" && set -- "$@" "--customize-hook=$SCRIPT" + +EXT4_OFFSET_BYTES=$(( (FAT_OFFSET_SECTORS + FAT_SIZE_SECTORS) * 512)) +EXT4_OPTIONS="offset=$EXT4_OFFSET_BYTES,assume_storage_prezeroed=1" +set -- "$@" \ + "--customize-hook=download vmlinuz '$WORKDIR/kernel'" \ + "--customize-hook=download initrd.img '$WORKDIR/initrd'" \ + '--customize-hook=mount --bind "$1" "$1/mnt"' \ + '--customize-hook=mount --bind "$1/mnt/mnt" "$1/mnt/dev"' \ + '--customize-hook=/sbin/mkfs.ext4 -d "$1/mnt" -L autopkgtestvm -E '"'$EXT4_OPTIONS' '$IMAGE' '$SIZE'" \ + '--customize-hook=umount --lazy "$1/mnt"' \ + "$RELEASE" \ + /dev/null + +test -n "$MIRROR" && set -- "$@" "$MIRROR" +test -n "$KEYRING" && set -- "$@" "--keyring=$KEYRING" + +echo "mmdebstrap $*" +mmdebstrap "$@" || die "mmdebstrap failed" + +unshare -U -r --map-groups=auto chown 0:0 "$IMAGE" +chmod "$(printf %o "$(( 0666 - 0$(umask) ))")" "$IMAGE" + +echo "root=LABEL=autopkgtestvm rw console=ttyS0" > "$WORKDIR/cmdline" + +align_size() { + echo "$(( ($1) + ($2) - 1 - (($1) + ($2) - 1) % ($2) ))" +} + +alignment=$("$GNU_ARCHITECTURE-objdump" -p "$BOOTSTUB" | sed 's/^SectionAlignment\s\+\([0-9]\)/0x/;t;d') +test -z "$alignment" && die "failed to discover the alignment of the efi stub" +echo "determined efi vma alignment as $alignment" +test "$RELEASE" = jessie -a "$((alignment))" -lt "$((1024*1024))" && { + echo "increasing efi vma alignment for jessie" + alignment=$((1024*1024)) +} +lastoffset=0 +# shellcheck disable=SC2034 # unused variables serve documentation +lastoffset="$("$GNU_ARCHITECTURE-objdump" -h "$BOOTSTUB" | + while read -r idx name size vma lma fileoff algn behind; do + test -z "$behind" -a "${algn#"2**"}" != "$algn" || continue + offset=$(( 0x$vma + 0x$size )) + test "$offset" -gt "$lastoffset" || continue + lastoffset="$offset" + echo "$lastoffset" + done | tail -n1)" +lastoffset=$(align_size "$lastoffset" "$alignment") +echo "determined minimum efi vma offset as $lastoffset" + +cmdline_size="$(stat -Lc%s "$WORKDIR/cmdline")" +cmdline_size="$(align_size "$cmdline_size" "$alignment")" +linux_size="$(stat -Lc%s "$WORKDIR/kernel")" +linux_size="$(align_size "$linux_size" "$alignment")" +cmdline_offset="$lastoffset" +linux_offset=$((cmdline_offset + cmdline_size)) +initrd_offset=$((linux_offset + linux_size)) + +SOURCE_DATE_EPOCH=0 \ + "$GNU_ARCHITECTURE-objcopy" \ + --enable-deterministic-archives \ + --add-section .cmdline="$WORKDIR/cmdline" \ + --change-section-vma .cmdline="$(printf 0x%x "$cmdline_offset")" \ + --add-section .linux="$WORKDIR/kernel" \ + --change-section-vma .linux="$(printf 0x%x "$linux_offset")" \ + --add-section .initrd="$WORKDIR/initrd" \ + --change-section-vma .initrd="$(printf 0x%x "$initrd_offset")" \ + "$BOOTSTUB" "$WORKDIR/efiimg" + +rm -f "$WORKDIR/kernel" "$WORKDIR/initrd" + +truncate -s "$((FAT_SIZE_SECTORS * 512))" "$WORKDIR/fat" +/sbin/mkfs.fat -F 32 --invariant "$WORKDIR/fat" +mmd -i "$WORKDIR/fat" EFI EFI/BOOT +mcopy -i "$WORKDIR/fat" "$WORKDIR/efiimg" "::EFI/BOOT/$EFIIMG" + +rm -f "$WORKDIR/efiimg" + +truncate --size="+$((34*512))" "$IMAGE" +/sbin/sfdisk "$IMAGE" <