From 2ed4d526b7ccc71ebf8443d1095730c906e47780 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Tue, 7 Jul 2026 12:38:19 +0100 Subject: [PATCH] build_library: Backport new initrd extraction script and adapt for zstd The older extraction script blew up on the latest kernel bump, seemingly because it is sensitive to cpio's handling of garbage data. The newer script handles this better, but I've had to adapt from xz back to zstd. For this latest bump, it now emits two rootfses when it should probably only emit one, but at least it doesn't blow up. It may be impossible to make this perfect, and it's only used for the difference report anyway. Signed-off-by: James Le Cuirot --- .../extract-initramfs-from-vmlinuz.sh | 105 +++++++----------- 1 file changed, 38 insertions(+), 67 deletions(-) diff --git a/build_library/extract-initramfs-from-vmlinuz.sh b/build_library/extract-initramfs-from-vmlinuz.sh index 9c1ffb6952b..39439a16115 100755 --- a/build_library/extract-initramfs-from-vmlinuz.sh +++ b/build_library/extract-initramfs-from-vmlinuz.sh @@ -7,51 +7,39 @@ # This will create one or more out-dir/rootfs-N directories that contain the contents of the initramfs. set -euo pipefail -# check for unzstd. Will abort the script with an error message if the tool is not present. -unzstd -V >/dev/null + +# check for zstdcat. Will abort the script with an error message if the tool is not present. +zstdcat -V >/dev/null + fail() { echo "${*}" >&2 exit 1 } -# Stolen from extract-vmlinux and modified. -try_decompress() { - local header="${1}" - local no_idea="${2}" - local tool="${3}" - local image="${4}" - local tmp="${5}" - local output_basename="${6}" - - local pos - local tool_filename=$(echo "${tool}" | cut -f1 -d' ') - # The obscure use of the "tr" filter is to work around older versions of - # "grep" that report the byte offset of the line instead of the pattern. +find_zstd_headers() { + grep --fixed-strings --text --byte-offset --only-matching $'\x28\xB5\x2F\xFD' "$1" | cut -d: -f1 +} - # Try to find the header and decompress from here. - for pos in $(tr "${header}\n${no_idea}" "\n${no_idea}=" < "${image}" | - grep --text --byte-offset --only-matching "^${no_idea}") - do - pos=${pos%%:*} - # Disable error handling, because we will be potentially - # giving the tool garbage or a valid archive with some garbage - # appended to it. So let the tool extract the valid archive - # and then complain about the garbage at the end, but don't - # fail the script because of it. - set +e; tail "-c+${pos}" "${image}" | "${tool}" >"${tmp}/out" 2>/dev/null; set -e; - if [ -s "${tmp}/out" ]; then - mv "${tmp}/out" "${output_basename}-${tool_filename}-at-${pos}" - else - rm -f "${tmp}/out" - fi - done +decompress_at() { + # Data may not really be a valid zstd, so allow for errors. + tail "-c+$((${2%:*} + 1))" "$1" | zstdcat 2>/dev/null || true } -try_unzstd_decompress() { - local image="${1}" - local tmp="${2}" - local output_basename="${3}" - try_decompress '(\265/\375' xxx unzstd "${image}" "${tmp}" "${output_basename}" +try_extract() { + # cpio can do strange things when given garbage, so do a basic check. + [[ $(head -c6 "$1") == 070701 ]] || return 0 + + while { + # cpio needs the directory to exist first. Fail if it's already there. + { mkdir "${out}/rootfs-${ROOTFS_IDX}" || return $?; } && + # There may be multiple concatenated archives so try cpio till it fails. + cpio --quiet --extract --make-directories --directory="${out}/rootfs-${ROOTFS_IDX}" --nonmatching 'dev/*' 2>/dev/null + }; do + ROOTFS_IDX=$(( ROOTFS_IDX + 1 )) + done < "$1" + + # Last cpio attempt may or may not leave an empty directory. + rmdir "${out}/rootfs-${ROOTFS_IDX}" 2>/dev/null || ROOTFS_IDX=$(( ROOTFS_IDX + 1 )) } me="${0##*/}" @@ -65,39 +53,22 @@ if [[ ! -s "${image}" ]]; then fi mkdir -p "${out}" -tmp=$(mktemp --directory /tmp/eifv-XXXXXX) -trap "rm -rf ${tmp}" EXIT +tmp=$(mktemp --directory -t eifv-XXXXXX) +trap 'rm -rf -- "${tmp}"' EXIT +ROOTFS_IDX=0 -tmp_dec="${tmp}/decompress" -mkdir "${tmp_dec}" -fr_prefix="${tmp}/first-round" +# arm64 kernels are not compressed, so try decompressing once. +# Other kernels are compressed, so also try decompressing twice. +for OFF1 in $(find_zstd_headers "${image}") +do + decompress_at "${image}" "${OFF1}" > "${tmp}/initrd.maybe_cpio_or_elf" + try_extract "${tmp}/initrd.maybe_cpio_or_elf" -ROOTFS_IDX=0 -perform_round() { - local image="${1}" - local tmp_dec="${2}" - local round_prefix="${3}" - try_unzstd_decompress "${image}" "${tmp_dec}" "${round_prefix}" - for rnd in "${round_prefix}"*; do - if [[ $(file --brief "${rnd}") =~ 'cpio archive' ]]; then - mkdir -p "${out}/rootfs-${ROOTFS_IDX}" - while cpio --quiet --extract --make-directories --directory="${out}/rootfs-${ROOTFS_IDX}" --nonmatching 'dev/*'; do - ROOTFS_IDX=$(( ROOTFS_IDX + 1 )) - mkdir -p "${out}/rootfs-${ROOTFS_IDX}" - done <${rnd} - rmdir "${out}/rootfs-${ROOTFS_IDX}" - fi + for OFF2 in $(find_zstd_headers "${tmp}/initrd.maybe_cpio_or_elf") + do + decompress_at "${tmp}/initrd.maybe_cpio_or_elf" "${OFF2}" > "${tmp}/initrd.maybe_cpio" + try_extract "${tmp}/initrd.maybe_cpio" done -} - -shopt -s nullglob -perform_round "${image}" "${tmp_dec}" "${fr_prefix}" -for fr in "${fr_prefix}"*; do - fr_files="${fr}-files" - fr_dec="${fr_files}/decompress" - mkdir -p "${fr_dec}" - sr_prefix="${fr_files}/second-round" - perform_round "${fr}" "${fr_dec}" "${sr_prefix}" done if [[ ${ROOTFS_IDX} -eq 0 ]]; then