pmaports/main/postmarketos-mkinitfs/mkinitfs.sh
Minecrell ed3eeeeebb
main/postmarketos-mkinitfs: append extra initfs hash to file contents (!983)
Partial revert of 24fa68c9 ("avoid storing initramfs-extra twice").

Some people have (rightfully) complained that including the hash
in the filename of the initramfs-extra prevents the device from booting
whenever the boot partition gets out of sync with the boot image.

In general we should assume that those two are in sync, but there is
no need to unnecessarily break this if previously booting the outdated
extra initramfs worked just fine.

At the end, the hash is just an implementation detail for the caching
mechanism - we need it when (re-)generating the initramfs, but it does
not have to be within the filename. Instead, we can simply append the
hash to the file contents. GZIP will simply ignore the trailing garbage.

The boot partition will then look like:
    $ cat /boot/initramfs-<flavor>-extra
    <GZIP data>d3c7b449c6fc811d97351bbc46852b66 (the hash)

This makes the filename of initramfs-extra nice and stable again.
It also fixes a regression where the initramfs-extra would no longer
get symlinked by "pmbootstrap export" (I was going to fix that in
pmbootstrap but now this solution is more appealing...)

While we're at it: Make the script more resilient again by writing
to a temporary file first, then move it (atomically) to the real path.
2020-03-01 00:31:27 +01:00

410 lines
11 KiB
Bash

#!/bin/sh
source_deviceinfo()
{
if [ ! -e "/etc/deviceinfo" ]; then
echo "NOTE: deviceinfo (from device package) not installed yet," \
"not building the initramfs now (it should get built later" \
"automatically.)"
exit 0
fi
. /etc/deviceinfo
}
parse_commandline()
{
if [ "$1" != "-o" ]; then
echo "postmarketos-mkinitfs"
echo "usage: $(basename "$0") -o OUTFILE KERNELVERSION"
exit 1
fi
outfile=$2
outfile_extra=$2-extra
kernel=$3
modules_path="/lib/modules/${kernel}"
}
# Verify that each file required by the installed hooks exists and exit with an
# error if they don't.
check_hook_files()
{
for file in "/etc/postmarketos-mkinitfs/files"/*.files; do
[ -f "$file" ] || continue
while IFS= read -r line; do
if ! [ -f "$line" ]; then
echo "ERROR: File ${line} specified in ${file} does not exist!"
exit 1
fi
done < "$file"
done
}
create_folders()
{
for dir in /bin /sbin /usr/bin /usr/sbin /proc /sys /dev /tmp /lib \
/boot /sysroot /etc; do
mkdir -p "$tmpdir$dir"
done
}
get_modules_by_globs()
{
globs="
# base.modules
kernel/drivers/block/loop.ko
kernel/fs/overlayfs
# cryptsetup.modules
kernel/crypto/*
kernel/arch/*/crypto/*
kernel/drivers/md/dm-crypt.ko
# required for modprobe
modules.*
"
for glob in $globs; do
for file in /lib/modules/$kernel/$glob; do
if [ -d "$file" ]; then
find "$file" -type f
elif [ -e "$file" ]; then
echo "$file"
fi
done
done
}
# NOTE: This does not work with busybox' modprobe
# That's why postmarketos-mkinitfs depends on kmod
get_modules_by_name()
{
{
echo "Scanning kernel module dependencies..."
echo "NOTE: ** modprobe warnings below can be ignored ** if your device does not run the"
echo "mainline kernel yet (most devices!) or if the related kernel options are enabled"
echo "with 'y' instead of 'm' (module)."
} >&2
MODULES="dm_crypt ext4 usb_f_rndis \
${deviceinfo_modules_initfs}"
modprobe \
-a \
--dry-run \
--show-depends \
--set-version="$kernel" \
$MODULES \
| sort -u \
| cut -d ' ' -f 2
}
get_modules()
{
get_modules_by_globs
get_modules_by_name
}
# Get the paths to all binaries and their dependencies
BINARIES="/bin/busybox /bin/busybox-extras /usr/sbin/telnetd /sbin/kpartx"
BINARIES_EXTRA="
$(find /usr/lib/directfb-* -name '*.so')
/lib/libz.so.1
/sbin/cryptsetup
/sbin/dmsetup
/sbin/e2fsck
/usr/bin/charging-sdl
/usr/bin/osk-sdl
/usr/lib/libGL.so.1
/usr/lib/libts*
/usr/lib/ts/*
/usr/sbin/parted
/usr/sbin/resize2fs
/usr/sbin/thd
"
get_binaries()
{
for file in "/etc/postmarketos-mkinitfs/files"/*.files; do
[ -f "$file" ] || continue
while IFS= read -r line; do
BINARIES="${BINARIES} ${line}"
done < "$file"
done
lddtree -l $BINARIES | sort -u
}
# Collect non-binary files for osk-sdl and its dependencies
# This gets called as $(get_osk_config), so the exit code can be checked/handled.
get_osk_config()
{
fontpath=$(awk '/^keyboard-font/{print $3}' /etc/osk.conf)
if [ ! -f $fontpath ]; then
exit 1
fi
ret="
/etc/osk.conf
/etc/ts.conf
/etc/pointercal
/etc/fb.modes
/etc/directfbrc
$fontpath
"
echo "${ret}"
}
get_binaries_extra()
{
tmp1=$(mktemp /tmp/mkinitfs.XXXXXX)
get_binaries > "$tmp1"
tmp2=$(mktemp /tmp/mkinitfs.XXXXXX)
lddtree -l $BINARIES_EXTRA | sort -u > "$tmp2"
ret=$(comm -13 "$tmp1" "$tmp2")
rm "$tmp1" "$tmp2"
echo "${ret}"
}
# Copy files to the destination specified
# FIXME: this is a performance bottleneck
# $1: files
# $2: destination
copy_files()
{
for file in $1; do
[ -e "$file" ] || continue
cp -a --parents "$file" "$2"
done
}
create_device_nodes()
{
mknod -m 666 "$tmpdir/dev/null" c 1 3
mknod -m 644 "$tmpdir/dev/random" c 1 8
mknod -m 644 "$tmpdir/dev/urandom" c 1 9
}
replace_init_variables()
{
sed -i "s:@INITRAMFS_EXTRA@:${outfile_extra}:g" "$tmpdir/init"
}
# Create a cpio image of the specified folder
# $1: folder
# $2: outfile
create_cpio_image()
{
cd "$1"
[ -z "$deviceinfo_initfs_compression" ] && deviceinfo_initfs_compression='gzip -1'
find . -print0 \
| cpio --quiet -o -H newc \
| $deviceinfo_initfs_compression > "$2"
}
# Required command check with useful error message
# $1: command (e.g. "mkimage")
# $2: package (e.g. "uboot-tools")
# $3: related deviceinfo variable (e.g. "generate_bootimg")
require_package()
{
[ "$(command -v "$1")" == "" ] || return
echo "ERROR: 'deviceinfo_$3' is set, but the package '$2' was not"
echo "installed! Please add '$2' to the depends= line of your device's"
echo "APKBUILD. See also: <https://postmarketos.org/deviceinfo>"
exit 1
}
# Legacy u-boot images
create_uboot_files()
{
arch="arm"
if [ "${deviceinfo_arch}" == "aarch64" ]; then
arch="arm64"
fi
[ "${deviceinfo_generate_legacy_uboot_initfs}" == "true" ] || return
require_package "mkimage" "uboot-tools" "generate_legacy_uboot_initfs"
echo "==> initramfs: creating uInitrd"
mkimage -A $arch -T ramdisk -C none -n uInitrd -d "$outfile" \
"${outfile/initramfs-/uInitrd-}" || exit 1
echo "==> kernel: creating uImage"
kernelfile="${outfile/initramfs-/vmlinuz-}"
if [ "${deviceinfo_append_dtb}" == "true" ]; then
kernelfile="${kernelfile}-dtb"
fi
if [ -z "$deviceinfo_legacy_uboot_load_address" ]; then
deviceinfo_legacy_uboot_load_address="80008000"
fi
mkimage -A $arch -O linux -T kernel -C none -a "$deviceinfo_legacy_uboot_load_address" \
-e "$deviceinfo_legacy_uboot_load_address" \
-n postmarketos -d $kernelfile "${outfile/initramfs-/uImage-}" || exit 1
}
# Android devices
create_bootimg()
{
[ "${deviceinfo_generate_bootimg}" == "true" ] || return
require_package "mkbootimg-osm0sis" "mkbootimg" "generate_bootimg"
echo "==> initramfs: creating boot.img"
_base="${deviceinfo_flash_offset_base}"
[ -z "$_base" ] && _base="0x10000000"
if [ "${deviceinfo_bootimg_mtk_mkimage}" == "true" ]; then
require_package "mtk-mkimage" "mtk-mkimage" "bootimg_mtk_mkimage"
mv $outfile $outfile-orig
mtk-mkimage ROOTFS $outfile-orig $outfile
fi
kernelfile="${outfile/initramfs-/vmlinuz-}"
if [ "${deviceinfo_append_dtb}" == "true" ]; then
kernelfile="${kernelfile}-dtb"
fi
_second=""
if [ "${deviceinfo_bootimg_dtb_second}" == "true" ]; then
if [ -z "${deviceinfo_dtb}" ]; then
echo "ERROR: deviceinfo_bootimg_dtb_second is set, but"
echo "'deviceinfo_dtb' is missing. Set 'deviceinfo_dtb'"
echo "to the device tree blob for your device."
echo "See also: <https://postmarketos.org/deviceinfo>"
exit 1
fi
dtb="/usr/share/dtb/${deviceinfo_dtb}.dtb"
_second="--second $dtb"
if ! [ -e "$dtb" ]; then
echo "ERROR: File not found: $dtb. Please set 'deviceinfo_dtb'"
echo "to the relative path to the device tree blob for your"
echo "device (without .dtb)."
echo "See also: <https://postmarketos.org/deviceinfo>"
exit 1
fi
fi
_dt=""
if [ "${deviceinfo_bootimg_qcdt}" == "true" ]; then
_dt="--dt /boot/dt.img"
if ! [ -e "/boot/dt.img" ]; then
echo "ERROR: File not found: /boot/dt.img, but"
echo "'deviceinfo_bootimg_qcdt' is set. Please verify that your"
echo "device is a QCDT device by analyzing the boot.img file"
echo "(e.g. 'pmbootstrap bootimg_analyze path/to/twrp.img')"
echo "and based on that, set the deviceinfo variable to false or"
echo "adjust your linux APKBUILD to properly generate the dt.img"
echo "file. See also: <https://postmarketos.org/deviceinfo>"
exit 1
fi
fi
mkbootimg-osm0sis \
--kernel "${kernelfile}" \
--ramdisk "$outfile" \
--base "${_base}" \
--second_offset "${deviceinfo_flash_offset_second}" \
--cmdline "${deviceinfo_kernel_cmdline}" \
--kernel_offset "${deviceinfo_flash_offset_kernel}" \
--ramdisk_offset "${deviceinfo_flash_offset_ramdisk}" \
--tags_offset "${deviceinfo_flash_offset_tags}" \
--pagesize "${deviceinfo_flash_pagesize}" \
${_second} \
${_dt} \
-o "${outfile/initramfs-/boot.img-}" || exit 1
if [ "${deviceinfo_bootimg_blobpack}" == "true" ]; then
echo "==> initramfs: creating blob"
blobpack "${outfile/initramfs-/blob-}" LNX \
"${outfile/initramfs-/boot.img-}" || exit 1
fi
if [ "${deviceinfo_bootimg_append_seandroidenforce}" == "true" ]; then
echo "==> initramfs: appending 'SEANDROIDENFORCE' to boot.img"
echo -n "SEANDROIDENFORCE" >> "${outfile/initramfs-/boot.img-}"
fi
}
# Append the correct device tree to the linux image file or copy the dtb to the boot partition
append_or_copy_dtb()
{
[ -n "${deviceinfo_dtb}" ] || return
dtb="/usr/share/dtb/${deviceinfo_dtb}.dtb"
kernel="${outfile/initramfs-/vmlinuz-}"
echo "==> kernel: device-tree blob operations"
if ! [ -e "$dtb" ]; then
echo "ERROR: File not found: $dtb"
exit 1
fi
if [ "${deviceinfo_append_dtb}" == "true" ]; then
echo "==> kernel: appending device-tree ${deviceinfo_dtb}"
cat "$kernel" "$dtb" > "${kernel}-dtb"
else
echo "==> kernel: copying dtb ${deviceinfo_dtb} to boot partition"
cp "$dtb" "$(dirname ${outfile})"
fi
}
# Create the initramfs-extra archive
# $1: outfile
generate_initramfs_extra()
{
echo "==> initramfs: creating $1"
osk_conf="$(get_osk_config)"
if [ $? -eq 1 ]; then
echo "ERROR: Font specified in /etc/osk.conf does not exist!"
exit 1
fi
# Hash all input files for caching
initfs_extra_files=$(echo "$BINARIES_EXTRA$osk_conf" | xargs -0 -I{} sh -c 'ls $1 2>/dev/null' -- {} | sort -u)
initfs_extra_files_hashes="$(md5sum $initfs_extra_files)"
initfs_extra_hash="$(echo "$initfs_extra_files_hashes" | md5sum | awk '{ print $1 }')"
# The hash is appended to the initramfs, check if up-to-date
if [ "$initfs_extra_hash" != "$(tail -c 32 "$1")" ]; then
# Set up initramfs-extra in temp folder
tmpdir_extra=$(mktemp -d /tmp/mkinitfs.XXXXXX)
mkdir -p "$tmpdir_extra"
copy_files "$(get_binaries_extra)" "$tmpdir_extra"
copy_files "$osk_conf" "$tmpdir_extra"
create_cpio_image "$tmpdir_extra" "$1.new"
rm -rf "$tmpdir_extra"
# Append hash to initramfs (used to check if up-to-date)
echo -n "$initfs_extra_hash" >> "$1.new"
# Replace old initramfs-extra *after* we are done to make sure
# it does not become corrupted if something goes wrong.
mv "$1.new" "$1"
fi
}
# initialize
source_deviceinfo
parse_commandline "$1" "$2" "$3"
check_hook_files
echo "==> initramfs: creating $outfile"
tmpdir=$(mktemp -d /tmp/mkinitfs.XXXXXX)
# set up initfs in temp folder
create_folders
copy_files "$(get_modules)" "$tmpdir"
copy_files "$(get_binaries)" "$tmpdir"
copy_files "/etc/deviceinfo" "$tmpdir"
copy_files "/etc/postmarketos-mkinitfs/hooks/*.sh" "$tmpdir"
cp /usr/share/postmarketos-splashes/*.ppm.gz "$tmpdir"
create_device_nodes
ln -s "/bin/busybox" "$tmpdir/bin/sh"
install -Dm755 "/usr/share/postmarketos-mkinitfs/init.sh.in" \
"$tmpdir/init"
install -Dm755 "/usr/share/postmarketos-mkinitfs/init_functions.sh" \
"$tmpdir/init_functions.sh"
# finish up
replace_init_variables
create_cpio_image "$tmpdir" "$outfile"
append_or_copy_dtb
create_uboot_files
create_bootimg
rm -rf "$tmpdir"
generate_initramfs_extra "$outfile_extra"
exit 0