Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 38 additions & 67 deletions build_library/extract-initramfs-from-vmlinuz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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##*/}"
Expand All @@ -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
Expand Down