mirror of
https://salsa.debian.org/kernel-team/initramfs-tools.git
synced 2026-01-26 15:39:08 +00:00
Bug #783620 involves several machines with root on xfs being upgraded from wheezy to jessie and then immediately rebooted. In some cases the initramfs was apparently written to the journal but not committed during the reboot. As GRUB doesn't replay the journal, it loaded an empty initramfs and the kernel failed to boot. There is clearly a bug elsewhere (init system or kernel) that prevented the filesystem being cleanly shut down. However, there seems to be a general risk here, so add a 'sync' to mitigate it. Closes: #783620 Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
491 lines
8.8 KiB
Bash
Executable File
491 lines
8.8 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
STATEDIR=/var/lib/initramfs-tools
|
|
BOOTDIR=/boot
|
|
CONF=/etc/initramfs-tools/update-initramfs.conf
|
|
USETRIGGERS=true
|
|
mode=""
|
|
version=""
|
|
update_initramfs=yes
|
|
backup_initramfs=no
|
|
|
|
set -e
|
|
|
|
[ -r ${CONF} ] && . ${CONF}
|
|
|
|
case "$DPKG_MAINTSCRIPT_PACKAGE" in
|
|
linux-image-*)
|
|
if [ -z "$INITRAMFS_TOOLS_KERNEL_HOOK" ]; then
|
|
# kernel maintainer script called us directly; ignore
|
|
# it and let the hook script handle it instead
|
|
echo "update-initramfs: deferring update (hook will be called later)"
|
|
exit 0
|
|
fi
|
|
;;
|
|
?*)
|
|
if $USETRIGGERS \
|
|
&& [ $# = 1 ] \
|
|
&& [ x"$1" = x-u ] \
|
|
&& dpkg-trigger --check-supported 2>/dev/null
|
|
then
|
|
if dpkg-trigger --no-await update-initramfs; then
|
|
echo "update-initramfs: deferring update (trigger activated)"
|
|
exit 0
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
usage()
|
|
{
|
|
if [ -n "${1:-}" ]; then
|
|
printf "${*}\n\n" >&2
|
|
fi
|
|
cat >&2 << EOF
|
|
Usage: ${0} [OPTION]...
|
|
|
|
Options:
|
|
-k version Specify kernel version or 'all'
|
|
-c Create a new initramfs
|
|
-u Update an existing initramfs
|
|
-d Remove an existing initramfs
|
|
-t Take over a custom initramfs with this one
|
|
-b directory Set alternate boot directory
|
|
-v Be verbose
|
|
-h This message
|
|
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# chroot check
|
|
chrooted()
|
|
{
|
|
# borrowed from udev's postinst
|
|
if [ "$(stat -c %d/%i /)" = "$(stat -Lc %d/%i /proc/1/root 2>/dev/null)" ]; then
|
|
# the devicenumber/inode pair of / is the same as that of
|
|
# /sbin/init's root, so we're *not* in a chroot and hence
|
|
# return false.
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
mild_panic()
|
|
{
|
|
if [ -n "${1:-}" ]; then
|
|
printf "${*}\n" >&2
|
|
fi
|
|
exit 0
|
|
}
|
|
|
|
panic()
|
|
{
|
|
if [ -n "${1:-}" ]; then
|
|
printf "${*}\n" >&2
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
verbose()
|
|
{
|
|
if [ "${verbose}" = 1 ]; then
|
|
printf "${*}\n"
|
|
fi
|
|
}
|
|
|
|
version_exists()
|
|
{
|
|
[ -e "${STATEDIR}/${1}" ] && [ -e "${initramfs}" ]
|
|
return $?
|
|
}
|
|
|
|
set_initramfs()
|
|
{
|
|
initramfs="${BOOTDIR}/initrd.img-${version}"
|
|
}
|
|
|
|
|
|
# backup initramfs while running
|
|
backup_initramfs()
|
|
{
|
|
[ ! -r "${initramfs}" ] && return 0
|
|
initramfs_bak="${initramfs}.dpkg-bak"
|
|
[ -r "${initramfs_bak}" ] && rm -f "${initramfs_bak}"
|
|
ln -f "${initramfs}" "${initramfs_bak}" \
|
|
|| cp -a "${initramfs}" "${initramfs_bak}"
|
|
verbose "Keeping ${initramfs_bak}"
|
|
}
|
|
|
|
# keep booted initramfs
|
|
backup_booted_initramfs()
|
|
{
|
|
initramfs_bak="${initramfs}.dpkg-bak"
|
|
|
|
# first time run thus no backup
|
|
[ ! -r "${initramfs_bak}" ] && return 0
|
|
|
|
# chroot with no /proc
|
|
[ ! -r /proc/uptime ] && rm -f "${initramfs_bak}" && return 0
|
|
|
|
# no kept backup wanted
|
|
[ "${backup_initramfs}" = "no" ] && rm -f "${initramfs_bak}" && return 0
|
|
|
|
# no backup yet
|
|
if [ ! -r "${initramfs}.bak" ]; then
|
|
mv -f ${initramfs_bak} "${initramfs}.bak"
|
|
verbose "Backup ${initramfs}.bak"
|
|
return 0
|
|
fi
|
|
|
|
# keep booted initramfs
|
|
boot_initramfs=
|
|
uptime_days=$(awk '{printf "%d", $1 / 3600 / 24}' /proc/uptime)
|
|
if [ -n "$uptime_days" ]; then
|
|
boot_initramfs=$(find "${initramfs}.bak" -mtime +${uptime_days})
|
|
fi
|
|
if [ -n "${boot_initramfs}" ]; then
|
|
mv -f "${initramfs_bak}" "${initramfs}.bak"
|
|
verbose "Backup ${initramfs}.bak"
|
|
return 0
|
|
fi
|
|
verbose "Removing current backup ${initramfs_bak}"
|
|
rm -f ${initramfs_bak}
|
|
}
|
|
|
|
# nuke generated copy
|
|
remove_initramfs_bak()
|
|
{
|
|
[ -z "${initramfs_bak:-}" ] && return 0
|
|
rm -f "${initramfs_bak}"
|
|
verbose "Removing ${initramfs_bak}"
|
|
}
|
|
|
|
|
|
generate_initramfs()
|
|
{
|
|
echo "update-initramfs: Generating ${initramfs}"
|
|
OPTS="-o"
|
|
if [ "${verbose}" = 1 ]; then
|
|
OPTS="-v ${OPTS}"
|
|
fi
|
|
if mkinitramfs ${OPTS} "${initramfs}.new" "${version}"; then
|
|
mv -f "${initramfs}.new" "${initramfs}"
|
|
set_sha1
|
|
# Guard against an unclean shutdown
|
|
sync
|
|
else
|
|
mkinitramfs_return="$?"
|
|
remove_initramfs_bak
|
|
rm -f "${initramfs}.new"
|
|
echo "update-initramfs: failed for ${initramfs} with $mkinitramfs_return." >&2
|
|
exit $mkinitramfs_return
|
|
fi
|
|
}
|
|
|
|
# Invoke bootloader
|
|
run_bootloader()
|
|
{
|
|
# invoke policy conformant bootloader hooks
|
|
if [ -d /etc/initramfs/post-update.d/ ]; then
|
|
run-parts --arg=${version} --arg=${initramfs} \
|
|
/etc/initramfs/post-update.d/
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
compare_sha1()
|
|
{
|
|
sha1sum "${initramfs}" | diff "${STATEDIR}/${version}" - >/dev/null 2>&1
|
|
return $?
|
|
}
|
|
|
|
# Note that this must overwrite so that updates work.
|
|
set_sha1()
|
|
{
|
|
sha1sum "${initramfs}" > "${STATEDIR}/${version}"
|
|
}
|
|
|
|
delete_sha1()
|
|
{
|
|
rm -f "${STATEDIR}/${version}"
|
|
}
|
|
|
|
# ro /boot is not modified
|
|
ro_boot_check()
|
|
{
|
|
# check irrelevant inside of a chroot
|
|
if [ ! -r /proc/mounts ] || chrooted; then
|
|
return 0
|
|
fi
|
|
|
|
boot_opts=$(awk '/boot/{if ((match($4, /^ro/) || match($4, /,ro/)) \
|
|
&& $2 == "/boot") print "ro"}' /proc/mounts)
|
|
if [ -n "${boot_opts}" ]; then
|
|
echo "WARNING: /boot is ro mounted."
|
|
echo "update-initramfs: Not updating ${initramfs}"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
get_sorted_versions()
|
|
{
|
|
version_list=""
|
|
|
|
for gsv_x in "${STATEDIR}"/*; do
|
|
gsv_x="$(basename "${gsv_x}")"
|
|
if [ "${gsv_x}" = '*' ]; then
|
|
return 0
|
|
fi
|
|
worklist=""
|
|
for gsv_i in $version_list; do
|
|
if dpkg --compare-versions "${gsv_x}" '>' "${gsv_i}" 2>/dev/null; then
|
|
worklist="${worklist} ${gsv_x} ${gsv_i}"
|
|
gsv_x=""
|
|
else
|
|
worklist="${worklist} ${gsv_i}"
|
|
fi
|
|
done
|
|
if [ "${gsv_x}" != "" ]; then
|
|
worklist="${worklist} ${gsv_x}"
|
|
fi
|
|
version_list="${worklist}"
|
|
done
|
|
|
|
verbose "Available versions: ${version_list}"
|
|
}
|
|
|
|
set_current_version()
|
|
{
|
|
if [ -f /boot/initrd.img-`uname -r` ]; then
|
|
version=`uname -r`
|
|
fi
|
|
}
|
|
|
|
set_linked_version()
|
|
{
|
|
linktarget=
|
|
if [ -e /initrd.img ] && [ -L /initrd.img ]; then
|
|
linktarget="$(basename "$(readlink /initrd.img)")"
|
|
fi
|
|
|
|
if [ -e /boot/initrd.img ] && [ -L /boot/initrd.img ]; then
|
|
linktarget="$(basename "$(readlink /boot/initrd.img)")"
|
|
fi
|
|
|
|
if [ -z "${linktarget}" ]; then
|
|
return
|
|
fi
|
|
|
|
version="${linktarget##initrd.img-}"
|
|
}
|
|
|
|
set_highest_version()
|
|
{
|
|
get_sorted_versions
|
|
if [ -z "${version_list}" ]; then
|
|
version=
|
|
return
|
|
fi
|
|
set -- ${version_list}
|
|
version=${1}
|
|
}
|
|
|
|
create()
|
|
{
|
|
if [ -z "${version}" ]; then
|
|
usage "Create mode requires a version argument"
|
|
fi
|
|
|
|
set_initramfs
|
|
|
|
if [ "${takeover}" = 0 ]; then
|
|
if version_exists "${version}"; then
|
|
panic "Cannot create version ${version}: already exists"
|
|
fi
|
|
|
|
if [ -e "${initramfs}" ]; then
|
|
panic "${initramfs} already exists, cannot create."
|
|
fi
|
|
fi
|
|
|
|
generate_initramfs
|
|
|
|
run_bootloader
|
|
}
|
|
|
|
update()
|
|
{
|
|
if [ "${update_initramfs}" = "no" ]; then
|
|
echo "update-initramfs: Not updating initramfs."
|
|
exit 0
|
|
fi
|
|
|
|
if [ -z "${version}" ]; then
|
|
set_highest_version
|
|
fi
|
|
|
|
if [ -z "${version}" ]; then
|
|
set_linked_version
|
|
fi
|
|
|
|
if [ -z "${version}" ]; then
|
|
set_current_version
|
|
fi
|
|
|
|
if [ -z "${version}" ]; then
|
|
verbose "Nothing to do, exiting."
|
|
exit 0
|
|
fi
|
|
|
|
set_initramfs
|
|
|
|
ro_boot_check
|
|
|
|
altered_check
|
|
|
|
backup_initramfs
|
|
|
|
generate_initramfs
|
|
|
|
run_bootloader
|
|
|
|
backup_booted_initramfs
|
|
}
|
|
|
|
delete()
|
|
{
|
|
if [ -z "${version}" ]; then
|
|
usage "Delete mode requires a version argument"
|
|
fi
|
|
|
|
set_initramfs
|
|
|
|
if [ "${takeover}" = 0 ]; then
|
|
if [ ! -e "${initramfs}" ]; then
|
|
panic "Cannot delete ${initramfs}, doesn't exist."
|
|
fi
|
|
|
|
if ! version_exists "${version}"; then
|
|
panic "Cannot delete version ${version}: Not created by this utility."
|
|
fi
|
|
fi
|
|
|
|
altered_check
|
|
|
|
echo "update-initramfs: Deleting ${initramfs}"
|
|
|
|
delete_sha1
|
|
|
|
rm -f "${initramfs}" "${initramfs}.bak"
|
|
}
|
|
|
|
# Check for update mode on existing and modified initramfs
|
|
altered_check()
|
|
{
|
|
# No check on takeover
|
|
[ "${takeover}" = 1 ] && return 0
|
|
if [ ! -e "${initramfs}" ]; then
|
|
mild_panic "${initramfs} does not exist. Cannot update."
|
|
fi
|
|
if ! compare_sha1; then
|
|
echo "update-initramfs: ${initramfs} has been altered." >&2
|
|
mild_panic "update-initramfs: Cannot update. Override with -t option."
|
|
fi
|
|
}
|
|
|
|
# Defaults
|
|
verbose=0
|
|
yes=0
|
|
# We default to takeover=1 in Ubuntu, but not Debian
|
|
takeover=0
|
|
|
|
##
|
|
|
|
while getopts "k:cudyvtb:h?" flag; do
|
|
case "${flag}" in
|
|
k)
|
|
version="${OPTARG}"
|
|
;;
|
|
c)
|
|
mode="c"
|
|
;;
|
|
d)
|
|
mode="d"
|
|
;;
|
|
u)
|
|
mode="u"
|
|
;;
|
|
v)
|
|
verbose="1"
|
|
;;
|
|
y)
|
|
yes="1"
|
|
;;
|
|
t)
|
|
takeover="1"
|
|
;;
|
|
b)
|
|
BOOTDIR="${OPTARG}"
|
|
if [ ! -d "${BOOTDIR}" ]; then
|
|
echo "Error: ${BOOTDIR} is not a directory." >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
h|?)
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
|
|
shift $((${OPTIND} - 1))
|
|
|
|
if [ $# -ne 0 ]; then
|
|
printf "Extra argument '%s'\n\n" "$1" >&2
|
|
usage
|
|
fi
|
|
|
|
# Validate arguments
|
|
if [ -z "${mode}" ]; then
|
|
usage "You must specify at least one of -c, -u, or -d."
|
|
fi
|
|
|
|
if [ "${version}" = "all" ] \
|
|
|| ( [ "${update_initramfs}" = "all" ] && [ -z "${version}" ] ); then
|
|
: FIXME check for --yes, and if not ask are you sure
|
|
get_sorted_versions
|
|
if [ -z "${version_list}" ]; then
|
|
verbose "Nothing to do, exiting."
|
|
exit 0
|
|
fi
|
|
|
|
OPTS="-b ${BOOTDIR}"
|
|
if [ "${verbose}" = "1" ]; then
|
|
OPTS="${OPTS} -v"
|
|
fi
|
|
if [ "${takeover}" = "1" ]; then
|
|
OPTS="${OPTS} -t"
|
|
fi
|
|
if [ "${yes}" = "1" ]; then
|
|
OPTS="${OPTS} -y"
|
|
fi
|
|
for u_version in ${version_list}; do
|
|
verbose "Execute: ${0} -${mode} -k \"${u_version}\" ${OPTS}"
|
|
"${0}" -${mode} -k "${u_version}" ${OPTS}
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
|
|
case "${mode}" in
|
|
c)
|
|
create
|
|
;;
|
|
d)
|
|
delete
|
|
;;
|
|
u)
|
|
update
|
|
;;
|
|
esac
|