From 6cb356d3837ef5a9637c2f6df1de7ef85196c280 Mon Sep 17 00:00:00 2001 From: tranch Date: Wed, 26 Aug 2026 10:41:01 +0800 Subject: fix(installer): configure the GPT auto-root type Resolve Anaconda target mounts to their backing partition before marking it as the bootc GPT auto-root partition. --- .github/workflows/validate-installer-iso.yml | 3 ++ installer/build.sh | 25 ++++++++++++++ installer/configure-target-gpt-root.sh | 40 ++++++++++++++++++++++ tests/test-configure-target-gpt-root.sh | 51 ++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100755 installer/configure-target-gpt-root.sh create mode 100755 tests/test-configure-target-gpt-root.sh diff --git a/.github/workflows/validate-installer-iso.yml b/.github/workflows/validate-installer-iso.yml index 5aa6335..2151a23 100644 --- a/.github/workflows/validate-installer-iso.yml +++ b/.github/workflows/validate-installer-iso.yml @@ -42,6 +42,9 @@ jobs: - name: Test Bazzite Deck identity patch run: tests/test-patch-bazzite-deck-identity.sh + - name: Test GPT root configuration + run: tests/test-configure-target-gpt-root.sh + - name: Validate Anaconda WebUI patch run: | fixture="$(mktemp)" diff --git a/installer/build.sh b/installer/build.sh index 3d6659f..205f18c 100755 --- a/installer/build.sh +++ b/installer/build.sh @@ -23,6 +23,7 @@ dnf --setopt=excludepkgs= install -y \ libblockdev-lvm \ dracut-live \ livesys-scripts \ + gdisk \ grub2-efi-x64 \ grub2-efi-x64-cdboot @@ -62,6 +63,10 @@ install -Dm0755 \ "${SCRIPT_DIR}/configure-target-grub.sh" \ /usr/libexec/aerocore-configure-target-grub +install -Dm0755 \ + "${SCRIPT_DIR}/configure-target-gpt-root.sh" \ + /usr/libexec/aerocore-configure-target-gpt-root + # Plasma's live-session launcher defaults to start-here, and Fedora keeps # LOGO=fedora-logo-icon in the base live environment. for size in 16x16 22x22 24x24 32x32 36x36 48x48 64x64 96x96 128x128 256x256; do @@ -132,6 +137,26 @@ mount --bind \${target_tmp} /var/tmp df -h /var/tmp \${target_tmp} %end +# bootc relies on systemd's GPT auto-root discovery on the first boot. Anaconda +# can leave the installed root partition with the generic Linux filesystem +# type, which makes /dev/gpt-auto-root unavailable even though the filesystem +# itself is healthy. Normalize the type before ostreecontainer deployment. +%pre-install --erroronfail --log=/tmp/aerocore-gpt-root.log +set -eux +target_root= +for root in /mnt/sysroot /mnt/sysimage /var/mnt/sysroot /var/mnt/sysimage; do + if mountpoint -q \${root}; then + target_root=\${root} + break + fi +done +if [ x\${target_root} = x ]; then + echo No mounted target system found for GPT root configuration >&2 + exit 1 +fi +/usr/libexec/aerocore-configure-target-gpt-root \${target_root} +%end + ostreecontainer --url=${INSTALL_IMAGE_PAYLOAD} --transport=containers-storage --no-signature-verification # The local containers-storage deployment is intentionally unverified because diff --git a/installer/configure-target-gpt-root.sh b/installer/configure-target-gpt-root.sh new file mode 100755 index 0000000..470821e --- /dev/null +++ b/installer/configure-target-gpt-root.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -euo pipefail + +target_root=${1:?usage: configure-target-gpt-root.sh TARGET_ROOT} + +source_device="$(findmnt --nofsroot -no SOURCE --target "${target_root}")" +# For a Btrfs subvolume, findmnt decorates the backing device with the +# subvolume path (for example, /dev/vda3[/root]). lsblk and the partition +# validation below require the undecorated block-device path. +source_device="${source_device%%\[*}" +# Anaconda can bind-mount the target at /mnt/sysroot. In that case findmnt's +# human-readable SOURCE is "root", even though the mounted filesystem still +# has the real target partition's device number. Resolve that number through +# lsblk before requiring a partition-backed source. +if [[ ! "${source_device}" =~ ^/dev/ ]]; then + source_maj_min="$(findmnt -no MAJ:MIN --target "${target_root}")" + source_device="$(lsblk -nrpo NAME,MAJ:MIN | awk -v wanted="${source_maj_min}" '$2 == wanted { print $1; exit }')" +fi + +case "${source_device}" in + /dev/*[0-9]) ;; + *) + echo "Could not resolve a partition-backed target root: ${source_device}" >&2 + exit 1 + ;; +esac + +parent_device="$(lsblk -no PKNAME "${source_device}")" +partition_number="$(lsblk -no PARTN "${source_device}")" +if [[ -z "${parent_device}" || -z "${partition_number}" ]]; then + echo "Could not resolve parent disk and partition number for ${source_device}" >&2 + exit 1 +fi + +disk="/dev/${parent_device}" +root_type_guid="4f68bce3-e8cd-4db1-96e7-fbcaf984b709" +echo "Setting ${source_device} (partition ${partition_number} on ${disk}) to GPT root-x86-64 type ${root_type_guid}" +sgdisk --typecode="${partition_number}:${root_type_guid}" "${disk}" +partprobe "${disk}" || true diff --git a/tests/test-configure-target-gpt-root.sh b/tests/test-configure-target-gpt-root.sh new file mode 100755 index 0000000..ed5e33e --- /dev/null +++ b/tests/test-configure-target-gpt-root.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +set -euo pipefail + +project_root="$(git rev-parse --show-toplevel)" +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT +mkdir -p "${tmp_dir}/bin" + +# shellcheck disable=SC2016 # The generated fixture expands this in its own process. +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'if [[ " $* " == *" -no SOURCE "* ]]; then' \ + ' [[ " $* " == *" --nofsroot "* ]] || exit 97' \ + ' printf "%s\\n" "${TEST_FINDMNT_SOURCE:?}"' \ + 'else' \ + " printf '252:3\\n'" \ + 'fi' > "${tmp_dir}/bin/findmnt" + +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'case " $* " in' \ + " *\" PKNAME \"*) printf 'vda\\n' ;;" \ + " *\" PARTN \"*) printf '3\\n' ;;" \ + " *\" PARTNUM \"*) exit 98 ;;" \ + " *) printf '/dev/vda3 252:3\\n' ;;" \ + 'esac' > "${tmp_dir}/bin/lsblk" + +# shellcheck disable=SC2016 # The generated fixture expands TEST_OUTPUT itself. +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'printf "%s\\n" "$*" > "${TEST_OUTPUT:?}"' > "${tmp_dir}/bin/sgdisk" + +printf '%s\n' '#!/usr/bin/env bash' 'exit 0' > "${tmp_dir}/bin/partprobe" + +chmod +x "${tmp_dir}/bin/"* + +run_fixture() { + local findmnt_source=$1 + + TEST_FINDMNT_SOURCE="${findmnt_source}" TEST_OUTPUT="${tmp_dir}/sgdisk.args" \ + PATH="${tmp_dir}/bin:${PATH}" \ + bash "${project_root}/installer/configure-target-gpt-root.sh" /mnt/sysroot + + grep -qx -- '--typecode=3:4f68bce3-e8cd-4db1-96e7-fbcaf984b709 /dev/vda' \ + "${tmp_dir}/sgdisk.args" +} + +run_fixture root +run_fixture '/dev/vda3[/root]' +printf 'GPT root bind-mount and Btrfs subvolume fixtures passed\n' -- cgit v1.2.3