Build Fedora aarch64 images for the Lenovo Yoga C630
Assembles a ready-to-write disk image via Gitea Actions. Mainline has carried sdm850-lenovo-yoga-c630.dts since 5.5 and Fedora ships it in kernel-core, so unlike aarch64-laptops/build there is no kernel or GRUB to compile — what is left is producing an image that boots on firmware which hands Linux no device tree. The build runs in an aarch64 container under qemu-user and builds filesystems from directory trees with mke2fs -d and mcopy rather than mounting loop devices, so it works on runners that will not hand out /dev/loop-control. A kernel-install hook writes the devicetree line into each BLS entry; without it the first `dnf update kernel` would produce an unbootable system. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
This commit is contained in:
116
build/build-image.sh
Executable file
116
build/build-image.sh
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/bash
|
||||
#
|
||||
# Host-side driver. Runs on an x86_64 CI runner (or your workstation) and does
|
||||
# the real work inside an aarch64 Fedora container under qemu-user emulation.
|
||||
#
|
||||
# ./build/build-image.sh --variant minimal
|
||||
#
|
||||
# Everything arch-specific happens in build/stage2.sh, which runs inside that
|
||||
# container. This script's only jobs are checking that emulation is wired up
|
||||
# and handing the container the right mounts.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$REPO_DIR"
|
||||
|
||||
VARIANT=minimal
|
||||
OUTPUT_DIR="$REPO_DIR/output"
|
||||
CONTAINER_IMAGE=""
|
||||
KEEP_ROOTFS=0
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [options]
|
||||
|
||||
--variant NAME Package variant from config/packages/ (default: minimal)
|
||||
--output DIR Where to write the image (default: ./output)
|
||||
--size MIB Image size in MiB (default: from config/device.env)
|
||||
--image REF Build container image (default: registry.fedoraproject.org/fedora:\$FEDORA_RELEASE)
|
||||
--keep-rootfs Leave the staged rootfs behind for inspection
|
||||
-h, --help This message
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--variant) VARIANT="$2"; shift 2 ;;
|
||||
--output) OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--size) export IMAGE_SIZE_MIB="$2"; shift 2 ;;
|
||||
--image) CONTAINER_IMAGE="$2"; shift 2 ;;
|
||||
--keep-rootfs) KEEP_ROOTFS=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown option: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# shellcheck source=../config/device.env
|
||||
source "$REPO_DIR/config/device.env"
|
||||
|
||||
: "${CONTAINER_IMAGE:=registry.fedoraproject.org/fedora:${FEDORA_RELEASE}}"
|
||||
|
||||
if [ ! -f "$REPO_DIR/config/packages/${VARIANT}.pkgs" ]; then
|
||||
echo "no such variant: ${VARIANT}" >&2
|
||||
echo "available: $(cd "$REPO_DIR/config/packages" && ls *.pkgs | sed 's/\.pkgs$//' | grep -v '^base$' | tr '\n' ' ')" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# --- emulation check ----------------------------------------------------
|
||||
#
|
||||
# Building an aarch64 rootfs means running aarch64 rpm scriptlets, which needs
|
||||
# a binfmt_misc handler registered in the *host* kernel. A container cannot
|
||||
# provide that for itself.
|
||||
if [ "$(uname -m)" != "$TARGET_ARCH" ]; then
|
||||
handler=/proc/sys/fs/binfmt_misc/qemu-aarch64
|
||||
if [ ! -e "$handler" ]; then
|
||||
cat >&2 <<EOF
|
||||
error: no binfmt_misc handler for aarch64.
|
||||
|
||||
This host is $(uname -m), so building an ${TARGET_ARCH} image needs qemu-user
|
||||
emulation registered with the kernel. On a Fedora runner, one-time setup:
|
||||
|
||||
sudo dnf install -y qemu-user-static-aarch64
|
||||
sudo systemctl restart systemd-binfmt
|
||||
|
||||
See docs/runner-setup.md.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
# The handler must be flagged F (fix binary), or the interpreter is looked
|
||||
# up inside the container's mount namespace, where it does not exist.
|
||||
if ! grep -q '^flags:.*F' "$handler"; then
|
||||
echo "error: $handler is registered without the 'F' flag; the qemu" >&2
|
||||
echo " interpreter will not be visible inside the container." >&2
|
||||
echo " Install qemu-user-static-aarch64 rather than qemu-user." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
command -v podman >/dev/null || { echo "error: podman not found" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
BUILD_REF="$(git -C "$REPO_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||
BUILD_DATE="$(date -u +%Y-%m-%d)"
|
||||
|
||||
echo "==> variant=${VARIANT} release=${FEDORA_RELEASE} arch=${TARGET_ARCH} ref=${BUILD_REF}"
|
||||
echo "==> build container: ${CONTAINER_IMAGE}"
|
||||
|
||||
# --privileged is what lets stage2 bind-mount /proc and /sys into the staged
|
||||
# rootfs so dracut can run in a chroot. Rootless podman grants only the caps
|
||||
# the invoking user already has inside their user namespace, so this is not
|
||||
# the escalation it looks like.
|
||||
exec podman run --rm \
|
||||
--arch arm64 \
|
||||
--privileged \
|
||||
--security-opt label=disable \
|
||||
-v "$REPO_DIR:/src:ro" \
|
||||
-v "$OUTPUT_DIR:/out" \
|
||||
-e VARIANT="$VARIANT" \
|
||||
-e FEDORA_RELEASE="$FEDORA_RELEASE" \
|
||||
-e IMAGE_SIZE_MIB="${IMAGE_SIZE_MIB}" \
|
||||
-e BUILD_REF="$BUILD_REF" \
|
||||
-e BUILD_DATE="$BUILD_DATE" \
|
||||
-e KEEP_ROOTFS="$KEEP_ROOTFS" \
|
||||
"$CONTAINER_IMAGE" \
|
||||
/bin/bash /src/build/stage2.sh
|
||||
337
build/stage2.sh
Executable file
337
build/stage2.sh
Executable file
@@ -0,0 +1,337 @@
|
||||
#!/usr/bin/bash
|
||||
#
|
||||
# Runs inside an aarch64 Fedora container (see build/build-image.sh).
|
||||
#
|
||||
# Stages a Fedora root filesystem with dnf, applies the C630 overlay, then
|
||||
# assembles a GPT disk image. Deliberately avoids loop devices: filesystems are
|
||||
# built from directory trees with `mke2fs -d` and `mcopy`, then dd'd into a
|
||||
# partitioned sparse file. That keeps the whole thing working on CI runners
|
||||
# where /dev/loop-control is not available to the job.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SRC=/src
|
||||
OUT=/out
|
||||
WORK="$OUT/.work"
|
||||
ROOTFS="$WORK/rootfs"
|
||||
|
||||
# shellcheck source=../config/device.env
|
||||
source "$SRC/config/device.env"
|
||||
|
||||
: "${VARIANT:=minimal}"
|
||||
: "${BUILD_REF:=unknown}"
|
||||
: "${BUILD_DATE:=unknown}"
|
||||
: "${KEEP_ROOTFS:=0}"
|
||||
|
||||
IMAGE_NAME="fedora-${FEDORA_RELEASE}-${VARIANT}-${DEVICE_NAME}-${BUILD_DATE}-${BUILD_REF}"
|
||||
IMAGE_PATH="$OUT/${IMAGE_NAME}.img"
|
||||
|
||||
log() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; }
|
||||
|
||||
MOUNTED=()
|
||||
unbind_all() {
|
||||
local i
|
||||
for (( i=${#MOUNTED[@]}-1; i>=0; i-- )); do
|
||||
umount "${MOUNTED[i]}" 2>/dev/null || umount -l "${MOUNTED[i]}" 2>/dev/null || true
|
||||
done
|
||||
MOUNTED=()
|
||||
}
|
||||
trap unbind_all EXIT
|
||||
|
||||
bind() { mount --bind "$1" "$2" && MOUNTED+=("$2"); }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Installing build tooling into the container"
|
||||
# ---------------------------------------------------------------------------
|
||||
dnf -y install --setopt=install_weak_deps=False \
|
||||
e2fsprogs dosfstools mtools gdisk util-linux rsync zstd findutils \
|
||||
>/dev/null
|
||||
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$ROOTFS" "$WORK/esp"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Resolving package list (base + ${VARIANT})"
|
||||
# ---------------------------------------------------------------------------
|
||||
read_pkgs() {
|
||||
# Strip comments and blank lines. '@^env' is dnf4 spelling for an
|
||||
# environment group; dnf5 wants a plain '@env'.
|
||||
sed -e 's/#.*//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e '/^$/d' \
|
||||
-e 's/^@\^/@/' "$1"
|
||||
}
|
||||
|
||||
mapfile -t PACKAGES < <(
|
||||
read_pkgs "$SRC/config/packages/base.pkgs"
|
||||
read_pkgs "$SRC/config/packages/${VARIANT}.pkgs"
|
||||
)
|
||||
echo "${#PACKAGES[@]} package specs"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Seeding repository configuration into the install root"
|
||||
# ---------------------------------------------------------------------------
|
||||
# dnf reads its repo definitions from inside --installroot. Seed them from the
|
||||
# container (same release, same arch) so the first transaction has somewhere to
|
||||
# fetch from and something to verify signatures against. The fedora-repos
|
||||
# package overwrites these with its own copies during the transaction.
|
||||
mkdir -p "$ROOTFS/etc/yum.repos.d" "$ROOTFS/etc/pki/rpm-gpg" "$ROOTFS/etc/dnf"
|
||||
cp -a /etc/yum.repos.d/. "$ROOTFS/etc/yum.repos.d/"
|
||||
cp -a /etc/pki/rpm-gpg/. "$ROOTFS/etc/pki/rpm-gpg/"
|
||||
if [ -d /etc/dnf/vars ]; then cp -a /etc/dnf/vars "$ROOTFS/etc/dnf/"; fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Installing Fedora ${FEDORA_RELEASE} (${TARGET_ARCH}) — this is the slow part"
|
||||
# ---------------------------------------------------------------------------
|
||||
dnf -y \
|
||||
--installroot="$ROOTFS" \
|
||||
--releasever="$FEDORA_RELEASE" \
|
||||
--setopt=keepcache=0 \
|
||||
--setopt=install_weak_deps=True \
|
||||
install "${PACKAGES[@]}"
|
||||
|
||||
KVER="$(rpm --root "$ROOTFS" -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}\n' \
|
||||
| sort -V | tail -1)"
|
||||
[ -n "$KVER" ] || { echo "could not determine installed kernel version" >&2; exit 1; }
|
||||
echo "kernel: $KVER"
|
||||
|
||||
if [ ! -e "$ROOTFS/boot/dtb-${KVER}/${DEVICE_DTB}" ]; then
|
||||
echo "error: ${DEVICE_DTB} is not in this kernel's device trees." >&2
|
||||
echo " Check DEVICE_DTB in config/device.env against:" >&2
|
||||
ls "$ROOTFS/boot/dtb-${KVER}/qcom/" | grep -i yoga >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Applying overlay"
|
||||
# ---------------------------------------------------------------------------
|
||||
render() {
|
||||
sed -e "s|@DEVICE_DTB@|${DEVICE_DTB}|g" \
|
||||
-e "s|@DEVICE_CMDLINE@|${DEVICE_CMDLINE}|g" \
|
||||
-e "s|@DEVICE_DESC@|${DEVICE_DESC}|g" \
|
||||
-e "s|@DEVICE_NAME@|${DEVICE_NAME}|g" \
|
||||
-e "s|@FEDORA_RELEASE@|${FEDORA_RELEASE}|g" \
|
||||
-e "s|@BUILD_REF@|${BUILD_REF}|g" \
|
||||
-e "s|@BUILD_DATE@|${BUILD_DATE}|g"
|
||||
}
|
||||
|
||||
while IFS= read -r rel; do
|
||||
src="$SRC/overlay/$rel"
|
||||
dst="$ROOTFS/$rel"
|
||||
if [[ "$rel" == *.in ]]; then
|
||||
dst="${dst%.in}"
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
render < "$src" > "$dst"
|
||||
else
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
cp "$src" "$dst"
|
||||
fi
|
||||
chmod --reference="$src" "$dst"
|
||||
done < <(cd "$SRC/overlay" && find . -type f -printf '%P\n')
|
||||
|
||||
# Locally-supplied firmware, if the operator dropped any in. Contents mirror
|
||||
# /usr/lib/firmware/updates/ and are gitignored — see docs/firmware.md.
|
||||
if compgen -G "$SRC/firmware/local/*" >/dev/null; then
|
||||
log "Baking in firmware from firmware/local/"
|
||||
mkdir -p "$ROOTFS/usr/lib/firmware/updates"
|
||||
rsync -a --exclude=.gitkeep "$SRC/firmware/local/" \
|
||||
"$ROOTFS/usr/lib/firmware/updates/"
|
||||
find "$ROOTFS/usr/lib/firmware/updates" -type f -printf ' %P\n'
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Generating identifiers and filesystem tables"
|
||||
# ---------------------------------------------------------------------------
|
||||
ROOT_UUID="$(uuidgen)"
|
||||
BOOT_UUID="$(uuidgen)"
|
||||
ESP_ID="$(od -An -tx1 -N4 /dev/urandom | tr -d ' \n' | tr 'a-f' 'A-F')"
|
||||
ESP_UUID="${ESP_ID:0:4}-${ESP_ID:4:4}"
|
||||
|
||||
cat > "$ROOTFS/etc/fstab" <<EOF
|
||||
UUID=${ROOT_UUID} / ext4 defaults 1 1
|
||||
UUID=${BOOT_UUID} /boot ext4 defaults 1 2
|
||||
UUID=${ESP_UUID} /boot/efi vfat umask=0077,shortname=winnt 0 2
|
||||
EOF
|
||||
|
||||
# Empty (not missing) machine-id marks this as a first boot for systemd, which
|
||||
# then generates a unique one per device rather than cloning the builder's.
|
||||
: > "$ROOTFS/etc/machine-id"
|
||||
|
||||
ln -sf ../run/systemd/resolve/stub-resolv.conf "$ROOTFS/etc/resolv.conf"
|
||||
echo "$DEVICE_NAME" > "$ROOTFS/etc/hostname"
|
||||
|
||||
# mke2fs -d does not reliably carry SELinux labels across, and the builder has
|
||||
# no policy loaded anyway. Relabel on first boot.
|
||||
: > "$ROOTFS/.autorelabel"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Configuring the target system"
|
||||
# ---------------------------------------------------------------------------
|
||||
bind /proc "$ROOTFS/proc"
|
||||
bind /sys "$ROOTFS/sys"
|
||||
bind /dev "$ROOTFS/dev"
|
||||
bind /dev/pts "$ROOTFS/dev/pts"
|
||||
|
||||
chroot "$ROOTFS" useradd -m -G wheel -s /bin/bash "$DEFAULT_USER"
|
||||
echo "${DEFAULT_USER}:${DEFAULT_PASSWORD}" | chroot "$ROOTFS" chpasswd
|
||||
chroot "$ROOTFS" chage -d 0 "$DEFAULT_USER" # force a change at first login
|
||||
chroot "$ROOTFS" passwd -l root
|
||||
|
||||
chroot "$ROOTFS" systemctl enable \
|
||||
c630-growfs.service \
|
||||
sshd.service \
|
||||
NetworkManager.service \
|
||||
systemd-resolved.service
|
||||
|
||||
if [ "$VARIANT" = workstation ]; then
|
||||
chroot "$ROOTFS" systemctl set-default graphical.target
|
||||
else
|
||||
chroot "$ROOTFS" systemctl set-default multi-user.target
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Building initramfs for ${KVER}"
|
||||
# ---------------------------------------------------------------------------
|
||||
chroot "$ROOTFS" dracut --force --no-hostonly --no-hostonly-cmdline \
|
||||
"/boot/initramfs-${KVER}.img" "$KVER"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Writing bootloader configuration"
|
||||
# ---------------------------------------------------------------------------
|
||||
# The bootstrap BLS entry. Subsequent kernels get theirs from kernel-install,
|
||||
# with the devicetree line supplied by 95-c630-devicetree.install.
|
||||
mkdir -p "$ROOTFS/boot/loader/entries"
|
||||
cat > "$ROOTFS/boot/loader/entries/c630-${KVER}.conf" <<EOF
|
||||
title Fedora Linux ${FEDORA_RELEASE} (${KVER}) — ${DEVICE_DESC}
|
||||
version ${KVER}
|
||||
linux /vmlinuz-${KVER}
|
||||
initrd /initramfs-${KVER}.img
|
||||
devicetree /dtb-${KVER}/${DEVICE_DTB}
|
||||
options root=UUID=${ROOT_UUID} ro ${DEVICE_CMDLINE}
|
||||
grub_users \$grub_users
|
||||
grub_arg --unrestricted
|
||||
grub_class fedora
|
||||
EOF
|
||||
|
||||
# A deliberately small grub.cfg. grub2-mkconfig would want to probe the running
|
||||
# system's block devices, which inside this container describe the builder, not
|
||||
# the C630. blscfg reads the entries above, so there is nothing else to do.
|
||||
mkdir -p "$ROOTFS/boot/grub2"
|
||||
cat > "$ROOTFS/boot/grub2/grub.cfg" <<EOF
|
||||
set timeout=5
|
||||
set default=0
|
||||
|
||||
insmod part_gpt
|
||||
insmod ext2
|
||||
insmod fat
|
||||
insmod all_video
|
||||
insmod gzio
|
||||
insmod blscfg
|
||||
|
||||
search --no-floppy --fs-uuid --set=root ${BOOT_UUID}
|
||||
|
||||
if [ -s \$prefix/grubenv ]; then
|
||||
load_env
|
||||
fi
|
||||
|
||||
blscfg
|
||||
EOF
|
||||
: > "$ROOTFS/boot/grub2/grubenv"
|
||||
|
||||
# ESP: GRUB at the removable-media path, because that is the only thing the
|
||||
# C630's firmware will find on a freshly written USB stick or SD card, and a
|
||||
# one-line stub telling it where the real configuration lives.
|
||||
GRUB_EFI="$ROOTFS/boot/efi/EFI/fedora/grubaa64.efi"
|
||||
[ -f "$GRUB_EFI" ] || { echo "grubaa64.efi missing from the install root" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$WORK/esp/EFI/BOOT" "$WORK/esp/EFI/fedora"
|
||||
cp "$GRUB_EFI" "$WORK/esp/EFI/BOOT/BOOTAA64.EFI"
|
||||
cp "$GRUB_EFI" "$WORK/esp/EFI/fedora/grubaa64.efi"
|
||||
if [ -f "$ROOTFS/boot/efi/EFI/fedora/shimaa64.efi" ]; then
|
||||
cp "$ROOTFS/boot/efi/EFI/fedora/shimaa64.efi" "$WORK/esp/EFI/fedora/"
|
||||
fi
|
||||
|
||||
# grubaa64.efi is built with a compiled-in prefix of /EFI/fedora, so this is
|
||||
# the file it looks for regardless of which path it was launched from.
|
||||
cat > "$WORK/esp/EFI/fedora/grub.cfg" <<EOF
|
||||
search --no-floppy --fs-uuid --set=dev ${BOOT_UUID}
|
||||
set root=\$dev
|
||||
set prefix=(\$dev)/grub2
|
||||
export prefix
|
||||
configfile \$prefix/grub.cfg
|
||||
EOF
|
||||
cp "$WORK/esp/EFI/fedora/grub.cfg" "$WORK/esp/EFI/BOOT/grub.cfg"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Splitting /boot out of the root tree"
|
||||
# ---------------------------------------------------------------------------
|
||||
# Non-lazily, so the bind-mounted trees are genuinely gone before mke2fs walks
|
||||
# the root filesystem — a lazy unmount would let it copy in the builder's /dev.
|
||||
unbind_all
|
||||
for d in dev/pts dev sys proc; do
|
||||
if mountpoint -q "$ROOTFS/$d"; then
|
||||
echo "error: $ROOTFS/$d is still mounted" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
mv "$ROOTFS/boot" "$WORK/boot"
|
||||
mkdir -p "$ROOTFS/boot"
|
||||
rm -rf "$WORK/boot/efi"
|
||||
mkdir -p "$WORK/boot/efi"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Partitioning ${IMAGE_SIZE_MIB} MiB image"
|
||||
# ---------------------------------------------------------------------------
|
||||
rm -f "$IMAGE_PATH"
|
||||
truncate -s "${IMAGE_SIZE_MIB}M" "$IMAGE_PATH"
|
||||
|
||||
sgdisk --zap-all "$IMAGE_PATH" >/dev/null
|
||||
sgdisk \
|
||||
--new "1:1M:+${ESP_SIZE_MIB}M" --typecode 1:ef00 --change-name 1:ESP \
|
||||
--new "2:0:+${BOOT_SIZE_MIB}M" --typecode 2:8300 --change-name 2:boot \
|
||||
--new "3:0:0" --typecode 3:8300 --change-name 3:root \
|
||||
"$IMAGE_PATH" >/dev/null
|
||||
sgdisk --print "$IMAGE_PATH"
|
||||
|
||||
part_first() { sgdisk --info="$1" "$IMAGE_PATH" | awk '/First sector/ {print $3}'; }
|
||||
part_last() { sgdisk --info="$1" "$IMAGE_PATH" | awk '/Last sector/ {print $3}'; }
|
||||
|
||||
ESP_START=$(part_first 1); ESP_SECTORS=$(( $(part_last 1) - ESP_START + 1 ))
|
||||
BOOT_START=$(part_first 2); BOOT_SECTORS=$(( $(part_last 2) - BOOT_START + 1 ))
|
||||
ROOT_START=$(part_first 3); ROOT_SECTORS=$(( $(part_last 3) - ROOT_START + 1 ))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Building filesystems from the staged trees"
|
||||
# ---------------------------------------------------------------------------
|
||||
# orphan_file and metadata_csum_seed are recent ext4 features that older GRUB
|
||||
# builds refuse to read. /boot has to be readable by whatever GRUB the firmware
|
||||
# ends up running, so keep both filesystems conservative.
|
||||
EXT4_OPTS="^orphan_file,^metadata_csum_seed"
|
||||
|
||||
mkfs.vfat -F 32 -n ESP -i "$ESP_ID" -C "$WORK/esp.img" $(( ESP_SECTORS / 2 )) >/dev/null
|
||||
mcopy -i "$WORK/esp.img" -s "$WORK/esp/EFI" ::
|
||||
|
||||
mke2fs -q -t ext4 -b 4096 -O "$EXT4_OPTS" -L boot -U "$BOOT_UUID" \
|
||||
-d "$WORK/boot" "$WORK/boot.img" $(( BOOT_SECTORS / 8 ))
|
||||
|
||||
mke2fs -q -t ext4 -b 4096 -O "$EXT4_OPTS" -L fedora -U "$ROOT_UUID" \
|
||||
-d "$ROOTFS" "$WORK/root.img" $(( ROOT_SECTORS / 8 ))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Assembling the disk image"
|
||||
# ---------------------------------------------------------------------------
|
||||
dd if="$WORK/esp.img" of="$IMAGE_PATH" bs=512 seek="$ESP_START" conv=notrunc,sparse status=none
|
||||
dd if="$WORK/boot.img" of="$IMAGE_PATH" bs=512 seek="$BOOT_START" conv=notrunc,sparse status=none
|
||||
dd if="$WORK/root.img" of="$IMAGE_PATH" bs=512 seek="$ROOT_START" conv=notrunc,sparse status=none
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Compressing"
|
||||
# ---------------------------------------------------------------------------
|
||||
zstd -12 -T0 --rm -f -o "${IMAGE_PATH}.zst" "$IMAGE_PATH"
|
||||
( cd "$OUT" && sha256sum "${IMAGE_NAME}.img.zst" > "${IMAGE_NAME}.img.zst.sha256" )
|
||||
|
||||
if [ "$KEEP_ROOTFS" != "1" ]; then
|
||||
rm -rf "$WORK"
|
||||
fi
|
||||
|
||||
log "Done"
|
||||
ls -lh "$OUT"
|
||||
Reference in New Issue
Block a user