Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dracut-initramfs-restore.sh): get compression method from image #2596

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
138 changes: 138 additions & 0 deletions dracut-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1081,3 +1081,141 @@ pe_get_image_base() {
[[ $? -eq 1 ]] && return 1
echo "$((16#$base_image))"
}

# get_dollar_boot
# $BOOT is the primary place to put boot menu entry resources into
# see https://uapi-group.org/specifications/specs/boot_loader_specification
get_dollar_boot() {
local _root_arg
local _esp
local _xbootldr
local _dollar_boot

if type -P bootctl &> /dev/null; then
if [[ -n $dracutsysrootdir ]]; then
_root_arg=(--root "$dracutsysrootdir")
fi
# shellcheck disable=SC2155 disable=SC2068
_esp=$(bootctl ${_root_arg[@]} -p 2> /dev/null)
# shellcheck disable=SC2155 disable=SC2068
_xbootldr=$(bootctl ${_root_arg[@]} -x 2> /dev/null)
[[ $_xbootldr == "$_esp" ]] && unset _xbootldr
_dollar_boot=${_xbootldr:-$_esp}
elif [[ -z $dracutsysrootdir ]]; then
if mountpoint -q /efi && [[ -d /efi/EFI ]]; then
_esp="/efi"
elif mountpoint -q /boot/efi && [[ -d /boot/efi/EFI ]]; then
_esp="/boot/efi"
fi
_dollar_boot=${_esp:-/boot}
else
if [[ -d "$dracutsysrootdir"/efi/EFI ]]; then
_esp="$dracutsysrootdir/efi"
elif [[ -d "$dracutsysrootdir"/boot/EFI ]]; then
_esp="$dracutsysrootdir/boot"
elif [[ -d "$dracutsysrootdir"/boot/efi/EFI ]]; then
_esp="$dracutsysrootdir/boot/efi"
fi
_dollar_boot=${_esp}
fi

echo -n "$_dollar_boot"
}

# get_machine_id [<$BOOT>|no]
get_machine_id() {
local _dollar_boot
local _machine_id

if [[ $1 != "no" ]]; then
_dollar_boot=${1:-$(get_dollar_boot)}
fi

if [[ $_dollar_boot ]] && [[ -d "$_dollar_boot"/Default ]]; then
_machine_id="Default"
elif [[ -s /etc/machine-id ]]; then
read -r _machine_id < /etc/machine-id
[[ $_machine_id == "uninitialized" ]] && _machine_id="Default"
else
_machine_id="Default"
fi

echo -n "$_machine_id"
}

# get_default_initramfs_image [<kernel_version>] [<$BOOT>|no] [<machine-id>|no]
get_default_initramfs_image() {
local _kver="$1"
local _dollar_boot
local _machine_id
local _image

[[ $_kver ]] || _kver="$(uname -r)"
if [[ $2 != "no" ]]; then
_dollar_boot=${2:-$(get_dollar_boot)}
fi
if [[ $3 != "no" ]]; then
_machine_id=${3:-$(get_machine_id "$_dollar_boot")}
fi

if [[ $_dollar_boot ]] && [[ $_machine_id ]] \
&& [[ -d "${_dollar_boot}"/loader/entries || -L "${_dollar_boot}"/loader/entries ]] \
&& [[ -d "${_dollar_boot}"/${_machine_id} || -L "${_dollar_boot}"/${_machine_id} ]]; then
_image="${_dollar_boot}/${_machine_id}/${_kver}/initrd"
elif [[ -f "$dracutsysrootdir"/lib/modules/${_kver}/initrd ]]; then
_image="$dracutsysrootdir/lib/modules/${_kver}/initrd"
elif [[ -f "$dracutsysrootdir"/lib/modules/${_kver}/initramfs.img ]]; then
_image="$dracutsysrootdir/lib/modules/${_kver}/initramfs.img"
else
_image="$dracutsysrootdir/boot/initramfs-${_kver}.img"
fi

echo -n "$_image"
}

# has_early_microcode <initramfs_image>
has_early_microcode() {
local _image="$1"
local _is_early
_is_early=$(cpio --extract --verbose --quiet --to-stdout -- 'early_cpio' < "$_image" 2> /dev/null)
# Debian mkinitramfs does not create the file 'early_cpio', so let's check if firmware files exist
[[ "$_is_early" ]] || _is_early=$(cpio --list --verbose --quiet --to-stdout -- 'kernel/*/microcode/*.bin' < "$_image" 2> /dev/null)
[[ "$_is_early" ]] && return 0
return 1
}

# get_decompression_command <initramfs_image_header>
get_decompression_command() {
local _bin="$1"
local _cmd

case $_bin in
$'\x1f\x8b'*)
_cmd="zcat --"
;;
BZh*)
_cmd="bzcat --"
;;
$'\x71\xc7'* | 070701)
_cmd="cat --"
;;
$'\x02\x21'*)
_cmd="lz4 -d -c"
;;
$'\x89'LZO$'\0'*)
_cmd="lzop -d -c"
;;
$'\x28\xB5\x2F\xFD'*)
_cmd="zstd -d -c"
;;
*)
if echo "test" | xz | xzcat --single-stream > /dev/null 2>&1; then
_cmd="xzcat --single-stream --"
else
_cmd="xzcat --"
fi
;;
esac

echo -n "$_cmd"
}
87 changes: 49 additions & 38 deletions dracut-initramfs-restore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,65 @@ set -e
# switching root to an incompletely unpacked initramfs
trap 'echo "Received SIGTERM signal, ignoring!" >&2' TERM

KERNEL_VERSION="$(uname -r)"

[[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut
SKIP="$dracutbasedir/skipcpio"
[[ -x $SKIP ]] || SKIP="cat"

if [[ -d /efi/Default ]] || [[ -d /boot/Default ]] || [[ -d /boot/efi/Default ]]; then
MACHINE_ID="Default"
elif [[ -s /etc/machine-id ]]; then
read -r MACHINE_ID < /etc/machine-id
[[ $MACHINE_ID == "uninitialized" ]] && MACHINE_ID="Default"

# shellcheck source=./dracut-functions.sh
. "$dracutbasedir"/dracut-functions.sh

mount -o ro /boot &> /dev/null || true

if [[ -f /sys/firmware/initrd ]]; then
IMG="/sys/firmware/initrd"
else
MACHINE_ID="Default"
# shellcheck disable=SC2119
IMG="$(get_default_initramfs_image)"
if [[ -z $IMG ]]; then
echo "No initramfs image found to restore!" >&2
exit 1
fi
fi

mount -o ro /boot &> /dev/null || true
# check if initramfs image contains early microcode and skip it
read -r -N 6 bin < "$IMG"
case $bin in
$'\x71\xc7'* | 070701)
CAT="cat --"
if has_early_microcode "$IMG"; then
SKIP="$dracutbasedir/skipcpio"
if ! [[ -x $SKIP ]]; then
echo "'$SKIP' not found, cannot skip early microcode to extract $IMG" >&2
exit 1
fi
fi
;;
esac

if [[ -d /efi/loader/entries || -L /efi/loader/entries ]] \
&& [[ -d /efi/$MACHINE_ID || -L /efi/$MACHINE_ID ]]; then
IMG="/efi/${MACHINE_ID}/${KERNEL_VERSION}/initrd"
elif [[ -d /boot/loader/entries || -L /boot/loader/entries ]] \
&& [[ -d /boot/$MACHINE_ID || -L /boot/$MACHINE_ID ]]; then
IMG="/boot/${MACHINE_ID}/${KERNEL_VERSION}/initrd"
elif [[ -d /boot/efi/loader/entries || -L /boot/efi/loader/entries ]] \
&& [[ -d /boot/efi/$MACHINE_ID || -L /boot/efi/$MACHINE_ID ]]; then
IMG="/boot/efi/$MACHINE_ID/$KERNEL_VERSION/initrd"
elif [[ -f /lib/modules/${KERNEL_VERSION}/initrd ]]; then
IMG="/lib/modules/${KERNEL_VERSION}/initrd"
elif [[ -f /boot/initramfs-${KERNEL_VERSION}.img ]]; then
IMG="/boot/initramfs-${KERNEL_VERSION}.img"
elif mountpoint -q /efi; then
IMG="/efi/$MACHINE_ID/$KERNEL_VERSION/initrd"
elif mountpoint -q /boot/efi; then
IMG="/boot/efi/$MACHINE_ID/$KERNEL_VERSION/initrd"
if [[ $SKIP ]]; then
bin="$($SKIP "$IMG" | { read -r -N 6 bin && echo "$bin"; })"
else
echo "No initramfs image found to restore!"
read -r -N 6 bin < "$IMG"
fi

# check if initramfs image is compressed
CAT=$(get_decompression_command "$bin")

type "${CAT%% *}" > /dev/null 2>&1 || {
echo "'${CAT%% *}' not found, cannot unpack $IMG" >&2
exit 1
}

skipcpio() {
$SKIP "$@" | $ORIG_CAT
}

if [[ $SKIP ]]; then
ORIG_CAT="$CAT"
CAT=skipcpio
fi

# decompress and extract initramfs image
cd /run/initramfs

if (command -v zcat > /dev/null && $SKIP "$IMG" 2> /dev/null | zcat 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| (command -v bzcat > /dev/null && $SKIP "$IMG" 2> /dev/null | bzcat 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| (command -v xzcat > /dev/null && $SKIP "$IMG" 2> /dev/null | xzcat 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| (command -v lz4 > /dev/null && $SKIP "$IMG" 2> /dev/null | lz4 -d -c 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| (command -v lzop > /dev/null && $SKIP "$IMG" 2> /dev/null | lzop -d -c 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| (command -v zstd > /dev/null && $SKIP "$IMG" 2> /dev/null | zstd -d -c 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1) \
|| ($SKIP "$IMG" 2> /dev/null | cpio -id --no-absolute-filenames --quiet > /dev/null 2>&1); then
if ($CAT "$IMG" | cpio -id --no-absolute-filenames --quiet > /dev/null); then
rm -f -- .need_shutdown
else
# something failed, so we clean up
Expand Down
113 changes: 36 additions & 77 deletions dracut.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1092,83 +1092,6 @@ drivers_dir="${drivers_dir%"${drivers_dir##*[!/]}"}"
[[ $sbat_l ]] && sbat="$sbat_l"
[[ $machine_id_l ]] && machine_id="$machine_id_l"

if ! [[ $outfile ]]; then
if [[ $machine_id != "no" ]]; then
if [[ -d "$dracutsysrootdir"/efi/Default ]] \
|| [[ -d "$dracutsysrootdir"/boot/Default ]] \
|| [[ -d "$dracutsysrootdir"/boot/efi/Default ]]; then
MACHINE_ID="Default"
elif [[ -s "$dracutsysrootdir"/etc/machine-id ]]; then
read -r MACHINE_ID < "$dracutsysrootdir"/etc/machine-id
[[ $MACHINE_ID == "uninitialized" ]] && MACHINE_ID="Default"
else
MACHINE_ID="Default"
fi
fi

if [[ $uefi == "yes" ]]; then
if [[ -n $uefi_secureboot_key && -z $uefi_secureboot_cert ]] || [[ -z $uefi_secureboot_key && -n $uefi_secureboot_cert ]]; then
printf "%s\n" "dracut[F]: Need 'uefi_secureboot_key' and 'uefi_secureboot_cert' both to be set." >&2
exit 1
fi

if [[ -n $uefi_secureboot_key && -n $uefi_secureboot_cert ]] && ! command -v sbsign &> /dev/null; then
printf "%s\n" "dracut[F]: Need 'sbsign' to create a signed UEFI executable." >&2
exit 1
fi

BUILD_ID=$(cat "$dracutsysrootdir"/etc/os-release "$dracutsysrootdir"/usr/lib/os-release \
| while read -r line || [[ $line ]]; do
[[ $line =~ BUILD_ID\=* ]] && eval "$line" && echo "$BUILD_ID" && break
done)
if [[ -z $dracutsysrootdir ]]; then
if [[ -d /efi ]] && mountpoint -q /efi; then
efidir=/efi/EFI
else
efidir=/boot/EFI
if [[ -d /boot/efi/EFI ]]; then
efidir=/boot/efi/EFI
fi
fi
else
efidir=/boot/EFI
if [[ -d $dracutsysrootdir/boot/efi/EFI ]]; then
efidir=/boot/efi/EFI
fi
fi
mkdir -p "$dracutsysrootdir$efidir/Linux"
outfile="$dracutsysrootdir$efidir/Linux/linux-$kernel${MACHINE_ID:+-${MACHINE_ID}}${BUILD_ID:+-${BUILD_ID}}.efi"
else
if [[ -d "$dracutsysrootdir"/efi/loader/entries || -L "$dracutsysrootdir"/efi/loader/entries ]] \
&& [[ $MACHINE_ID ]] \
&& [[ -d "$dracutsysrootdir"/efi/${MACHINE_ID} || -L "$dracutsysrootdir"/efi/${MACHINE_ID} ]]; then
outfile="$dracutsysrootdir/efi/${MACHINE_ID}/${kernel}/initrd"
elif [[ -d "$dracutsysrootdir"/boot/loader/entries || -L "$dracutsysrootdir"/boot/loader/entries ]] \
&& [[ $MACHINE_ID ]] \
&& [[ -d "$dracutsysrootdir"/boot/${MACHINE_ID} || -L "$dracutsysrootdir"/boot/${MACHINE_ID} ]]; then
outfile="$dracutsysrootdir/boot/${MACHINE_ID}/${kernel}/initrd"
elif [[ -d "$dracutsysrootdir"/boot/efi/loader/entries || -L "$dracutsysrootdir"/boot/efi/loader/entries ]] \
&& [[ $MACHINE_ID ]] \
&& [[ -d "$dracutsysrootdir"/boot/efi/${MACHINE_ID} || -L "$dracutsysrootdir"/boot/efi/${MACHINE_ID} ]]; then
outfile="$dracutsysrootdir/boot/efi/${MACHINE_ID}/${kernel}/initrd"
elif [[ -f "$dracutsysrootdir"/lib/modules/${kernel}/initrd ]]; then
outfile="$dracutsysrootdir/lib/modules/${kernel}/initrd"
elif [[ -e $dracutsysrootdir/boot/vmlinuz-${kernel} ]]; then
outfile="$dracutsysrootdir/boot/initramfs-${kernel}.img"
elif [[ -z $dracutsysrootdir ]] \
&& [[ $MACHINE_ID ]] \
&& mountpoint -q /efi; then
outfile="/efi/${MACHINE_ID}/${kernel}/initrd"
elif [[ -z $dracutsysrootdir ]] \
&& [[ $MACHINE_ID ]] \
&& mountpoint -q /boot/efi; then
outfile="/boot/efi/${MACHINE_ID}/${kernel}/initrd"
else
outfile="$dracutsysrootdir/boot/initramfs-${kernel}.img"
fi
fi
fi

# eliminate IFS hackery when messing with fw_dir
export DRACUT_FIRMWARE_PATH=${fw_dir// /:}
fw_dir=${fw_dir//:/ }
Expand Down Expand Up @@ -1313,6 +1236,42 @@ else
exit 1
fi

if ! [[ $outfile ]]; then
DOLLAR_BOOT="$(get_dollar_boot)"
[[ $DOLLAR_BOOT ]] && ddebug "\$BOOT set to $DOLLAR_BOOT"

if [[ $machine_id != "no" ]]; then
MACHINE_ID="$(get_machine_id "${DOLLAR_BOOT:-no}")"
fi

if [[ $uefi == "yes" ]]; then
if ! [[ $DOLLAR_BOOT ]]; then
# shellcheck disable=SC2016
dfatal '$BOOT (ESP and XBOOTLDR) partition not found.'
exit 1
fi

if [[ -n $uefi_secureboot_key && -z $uefi_secureboot_cert ]] || [[ -z $uefi_secureboot_key && -n $uefi_secureboot_cert ]]; then
dfatal "Need 'uefi_secureboot_key' and 'uefi_secureboot_cert' both to be set."
exit 1
fi

if [[ -n $uefi_secureboot_key && -n $uefi_secureboot_cert ]] && ! command -v sbsign &> /dev/null; then
dfatal "Need 'sbsign' to create a signed UEFI executable."
exit 1
fi

BUILD_ID=$(cat "$dracutsysrootdir"/etc/os-release "$dracutsysrootdir"/usr/lib/os-release \
| while read -r line || [[ $line ]]; do
[[ $line =~ BUILD_ID\=* ]] && eval "$line" && echo "$BUILD_ID" && break
done)
mkdir -p "${DOLLAR_BOOT}/EFI/Linux"
outfile="${DOLLAR_BOOT}/EFI/Linux/linux-$kernel${MACHINE_ID:+-${MACHINE_ID}}${BUILD_ID:+-${BUILD_ID}}.efi"
else
outfile="$(get_default_initramfs_image "$kernel" "${DOLLAR_BOOT:-no}" "${MACHINE_ID:-no}")"
fi
fi

if [[ $persistent_policy == "mapper" ]]; then
unset persistent_policy
elif [[ -n $persistent_policy && ! -d "/dev/disk/${persistent_policy}" ]]; then
Expand Down
Loading
Loading