Add support for backing up the old kernel when installing a new kernel

using freebsd-update.  This applies to using freebsd-update in "upgrade
mode" and normal freebsd-update on a security branch.

The backup kernel will be written to /boot/kernel.old, if the directory
does not exist, or the directory was created by freebsd-update in a
previous backup.  Otherwise freebsd-update will generate a new directory
name for use by the backup.  By default symbol files are not backed up
to save diskspace and avoid filling up the root partition.

This feature is fully configurable in the freebsd-update config file,
but defaults to enabled.

MFC after:	1 week (stable/7)
Reviewed by:	cperciva
Approved by:	re (kib)
This commit is contained in:
simon 2009-08-19 20:47:31 +00:00
parent 0c0a18c1ff
commit 5bb5dcc9da
3 changed files with 222 additions and 3 deletions

View File

@ -63,3 +63,14 @@ MergeChanges /etc/ /var/named/etc/ /boot/device.hints
# which *might* be installed of which FreeBSD Update should figure out
# which actually are installed and upgrade those (StrictComponents no)?
# StrictComponents no
# When installing a new kernel perform a backup of the old one first
# so it is possible to boot the old kernel in case of problems.
# BackupKernel yes
# If BackupKernel is enabled, the backup kernel is saved to this
# directory.
# BackupKernelDir /boot/kernel.old
# When backing up a kernel also back up debug symbol files?
# BackupKernelSymbolFiles no

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 30, 2006
.Dd August 19, 2009
.Dt FREEBSD-UPDATE.CONF 5
.Os FreeBSD
.Sh NAME
@ -48,7 +48,7 @@ error.
.Pp
The possible options and their meanings are as follows:
.Pp
.Bl -tag -width "KeepModifiedMetadata"
.Bl -tag -width ".Cm BackupKernelSymbolFiles"
.It Cm KeyPrint
The single parameter following this keyword is the SHA256 hash
of the RSA key which will be trusted to sign updates.
@ -171,6 +171,54 @@ command is used ("yes"), or merely as a list of components
which might be installed, of which
.Cm freebsd-update
should identify which in fact are present ("no").
.It Cm BackupKernel
The single parameter following this keyword must be
.Dq yes
or
.Dq no
and specifies whether
.Cm freebsd-update
will create a backup of the old kernel before installing a new kernel.
This backup kernel can be used to recover a system where the newly
installed kernel somehow did not work.
Note that the backup kernel is not reverted to its original state by
the
.Cm freebsd-update
rollback command.
.It Cm BackupKernelDir
This keyword sets the directory which is used to store a backup
kernel, if the BackupKernel feature is enabled.
If the directory already exist, and it was not created by
.Cm freebsd-update ,
the directory is skipped.
In the case of the primary directory name not being usable, a number
starting with
.Sq 1
is appended to the directory name.
Like with the primary directory name, the constructed directory name is
only used if the path name does not exist, or if the directory was
previously created by
.Cm freebsd-update .
If the constructed directory still exist the appended number is
incremented with 1 and the directory search process restarted.
Should the number increment go above 9,
.Cm freebsd-update
will abort.
.It Cm BackupKernelSymbolFiles
The single parameter following this keyword must be
.Dq yes
or
.Dq no
and specifies whether
.Cm freebsd-update
will also backup kernel symbol files, if they exist.
The kernel symbol files takes up a lot of disk space and are not
needed for recovery purposes.
If the symbol files are needed, after recovering a system using the
backup kernel, the
.Cm freebsd-update
rollback command will recreate the symbol files along with the old
kernel.
.El
.Sh FILES
.Bl -tag -width "/etc/freebsd-update.conf"

View File

@ -88,7 +88,7 @@ EOF
CONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
IDSIGNOREPATHS"
IDSIGNOREPATHS BACKUPKERNEL BACKUPKERNELDIR BACKUPKERNELSYMBOLFILES"
# Set all the configuration options to "".
nullconfig () {
@ -308,6 +308,70 @@ config_VerboseLevel () {
fi
}
config_BackupKernel () {
if [ -z ${BACKUPKERNEL} ]; then
case $1 in
[Yy][Ee][Ss])
BACKUPKERNEL=yes
;;
[Nn][Oo])
BACKUPKERNEL=no
;;
*)
return 1
;;
esac
else
return 1
fi
}
config_BackupKernelDir () {
if [ -z ${BACKUPKERNELDIR} ]; then
if [ -z "$1" ]; then
echo "BackupKernelDir set to empty dir"
return 1
fi
# We check for some paths which would be extremely odd
# to use, but which could cause a lot of problems if
# used.
case $1 in
/|/bin|/boot|/etc|/lib|/libexec|/sbin|/usr|/var)
echo "BackupKernelDir set to invalid path $1"
return 1
;;
/*)
BACKUPKERNELDIR=$1
;;
*)
echo "BackupKernelDir ($1) is not an absolute path"
return 1
;;
esac
else
return 1
fi
}
config_BackupKernelSymbolFiles () {
if [ -z ${BACKUPKERNELSYMBOLFILES} ]; then
case $1 in
[Yy][Ee][Ss])
BACKUPKERNELSYMBOLFILES=yes
;;
[Nn][Oo])
BACKUPKERNELSYMBOLFILES=no
;;
*)
return 1
;;
esac
else
return 1
fi
}
# Handle one line of configuration
configline () {
if [ $# -eq 0 ]; then
@ -461,6 +525,9 @@ default_params () {
config_BaseDir /
config_VerboseLevel stats
config_StrictComponents no
config_BackupKernel yes
config_BackupKernelDir /boot/kernel.old
config_BackupKernelSymbolFiles no
# Merge these defaults into the earlier-configured settings
mergeconfig
@ -665,6 +732,14 @@ install_check_params () {
echo "Re-run '$0 fetch'."
exit 1
fi
# Figure out what directory contains the running kernel
BOOTFILE=`sysctl -n kern.bootfile`
KERNELDIR=${BOOTFILE%/kernel}
if ! [ -d ${KERNELDIR} ]; then
echo "Cannot identify running kernel"
exit 1
fi
}
# Perform sanity checks and set some final parameters in
@ -2494,6 +2569,88 @@ install_unschg () {
rm filelist
}
# Decide which directory name to use for kernel backups.
backup_kernel_finddir () {
CNT=0
while true ; do
# Pathname does not exist, so it is OK use that name
# for backup directory.
if [ ! -e $BACKUPKERNELDIR ]; then
return 0
fi
# If directory do exist, we only use if it has our
# marker file.
if [ -d $BACKUPKERNELDIR -a \
-e $BACKUPKERNELDIR/.freebsd-update ]; then
return 0
fi
# We could not use current directory name, so add counter to
# the end and try again.
CNT=$((CNT + 1))
if [ $CNT -gt 9 ]; then
echo "Could not find valid backup dir ($BACKUPKERNELDIR)"
exit 1
fi
BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`"
BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}"
done
}
# Backup the current kernel using hardlinks, if not disabled by user.
# Since we delete all files in the directory used for previous backups
# we create a marker file called ".freebsd-update" in the directory so
# we can determine on the next run that the directory was created by
# freebsd-update and we then do not accidentally remove user files in
# the unlikely case that the user has created a directory with a
# conflicting name.
backup_kernel () {
# Only make kernel backup is so configured.
if [ $BACKUPKERNEL != yes ]; then
return 0
fi
# Decide which directory name to use for kernel backups.
backup_kernel_finddir
# Remove old kernel backup files. If $BACKUPKERNELDIR was
# "not ours", backup_kernel_finddir would have exited, so
# deleting the directory content is as safe as we can make it.
if [ -d $BACKUPKERNELDIR ]; then
rm -f $BACKUPKERNELDIR/*
fi
# Create directory for backup if it doesn't exist.
mkdir -p $BACKUPKERNELDIR
# Mark the directory as having been created by freebsd-update.
touch $BACKUPKERNELDIR/.freebsd-update
if [ $? -ne 0 ]; then
echo "Could not create kernel backup directory"
exit 1
fi
# Disable pathname expansion to be sure *.symbols is not
# expanded.
set -f
# Use find to ignore symbol files, unless disabled by user.
if [ $BACKUPKERNELSYMBOLFILES = yes ]; then
FINDFILTER=""
else
FINDFILTER=-"a ! -name *.symbols"
fi
# Backup all the kernel files using hardlinks.
find $KERNELDIR -type f $FINDFILTER | \
sed -Ee "s,($KERNELDIR)/?(.*),\1/\2 ${BACKUPKERNELDIR}/\2," | \
xargs -n 2 cp -pl
# Re-enable patchname expansion.
set +f
}
# Install new files
install_from_index () {
# First pass: Do everything apart from setting file flags. We
@ -2575,6 +2732,9 @@ install_files () {
grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
# Backup current kernel before installing a new one
backup_kernel || return 1
# Install new files
install_from_index INDEX-NEW || return 1