Add bootfs.snapshot and bootfs.rollback kernel parameters

Unlike other filesystems, snapshots and rollbacks of bootfs need to be
done from a rescue environment. This patch makes it possible to snap-
shot or rollback the bootfs simply by specifying bootfs.snapshot or
bootfs.rollback on the kernel command line. The operation will be
performed by dracut just before bootfs is mounted.

Reviewed-by: Antonio Russo <antonio.e.russo@gmail.com> 
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Gregory Bartholomew <gregory.lee.bartholomew@gmail.com>
Closes #10198
This commit is contained in:
gregory-lee-bartholomew 2020-05-29 23:16:08 -05:00 committed by GitHub
parent 3d93161b01
commit 9052e3d70b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 2 deletions

View File

@ -7,3 +7,5 @@ zfs-lib.sh
zfs-load-key.sh
zfs-needshutdown.sh
zfs-env-bootfs.service
zfs-snapshot-bootfs.service
zfs-rollback-bootfs.service

View File

@ -10,7 +10,9 @@ pkgdracut_SCRIPTS = \
zfs-lib.sh
pkgdracut_DATA = \
zfs-env-bootfs.service
zfs-env-bootfs.service \
zfs-snapshot-bootfs.service \
zfs-rollback-bootfs.service
EXTRA_DIST = \
$(top_srcdir)/contrib/dracut/90zfs/export-zfs.sh.in \
@ -21,7 +23,9 @@ EXTRA_DIST = \
$(top_srcdir)/contrib/dracut/90zfs/zfs-load-key.sh.in \
$(top_srcdir)/contrib/dracut/90zfs/zfs-needshutdown.sh.in \
$(top_srcdir)/contrib/dracut/90zfs/zfs-lib.sh.in \
$(top_srcdir)/contrib/dracut/90zfs/zfs-env-bootfs.service.in
$(top_srcdir)/contrib/dracut/90zfs/zfs-env-bootfs.service.in \
$(top_srcdir)/contrib/dracut/90zfs/zfs-snapshot-bootfs.service.in \
$(top_srcdir)/contrib/dracut/90zfs/zfs-rollback-bootfs.service.in
$(pkgdracut_SCRIPTS) $(pkgdracut_DATA) :%:%.in
-$(SED) -e 's,@bindir\@,$(bindir),g' \

View File

@ -109,5 +109,11 @@ install() {
ln -s ../zfs-import.target "${initdir}/$systemdsystemunitdir/initrd.target.wants"/zfs-import.target
type mark_hostonly >/dev/null 2>&1 && mark_hostonly @systemdunitdir@/zfs-import.target
fi
for _service in zfs-snapshot-bootfs.service zfs-rollback-bootfs.service ; do
inst "${moddir}"/$_service "${systemdsystemunitdir}"/$_service
if ! [ -L "${initdir}/$systemdsystemunitdir/initrd.target.wants"/$_service ]; then
ln -s ../$_service "${initdir}/$systemdsystemunitdir/initrd.target.wants"/$_service
fi
done
fi
}

View File

@ -0,0 +1,14 @@
[Unit]
Description=Rollback bootfs just before it is mounted
Requisite=zfs-import.target
After=zfs-import.target zfs-snapshot-bootfs.service
Before=dracut-mount.service
DefaultDependencies=no
ConditionKernelCommandLine=bootfs.rollback
[Service]
# ${BOOTFS} should have been set by zfs-env-bootfs.service
Type=oneshot
ExecStartPre=/bin/sh -c 'test -n "${BOOTFS}"'
ExecStart=/bin/sh -c '. /lib/dracut-lib.sh; SNAPNAME="$(getarg bootfs.rollback)"; /sbin/zfs rollback -Rf "${BOOTFS}@${SNAPNAME:-%v}"'
RemainAfterExit=yes

View File

@ -0,0 +1,14 @@
[Unit]
Description=Snapshot bootfs just before it is mounted
Requisite=zfs-import.target
After=zfs-import.target
Before=dracut-mount.service
DefaultDependencies=no
ConditionKernelCommandLine=bootfs.snapshot
[Service]
# ${BOOTFS} should have been set by zfs-env-bootfs.service
Type=oneshot
ExecStartPre=/bin/sh -c 'test -n "${BOOTFS}"'
ExecStart=-/bin/sh -c '. /lib/dracut-lib.sh; SNAPNAME="$(getarg bootfs.snapshot)"; /sbin/zfs snapshot "${BOOTFS}@${SNAPNAME:-%v}"'
RemainAfterExit=yes

View File

@ -59,6 +59,30 @@ to recover from this, you may use the `zfs_force` option or boot from a
different filesystem and `zpool import -f` then `zpool export` the pool
before rebooting with the new hostid.
* `bootfs.snapshot`: If listed, enables the zfs-snapshot-bootfs service on a Dracut system. The zfs-snapshot-bootfs service simply runs `zfs snapshot $BOOTFS@%v` after the pool has been imported but before the bootfs is mounted. `$BOOTFS` is substituted with the value of the bootfs setting on the pool. `%v` is substituted with the version string of the kernel currently being booted (e.g. 5.6.6-200.fc31.x86\_64). Failure to create the snapshot (e.g. because one with the same name already exists) will be logged, but will not otherwise interrupt the boot process.
It is safe to leave the bootfs.snapshot flag set persistently on your kernel command line so that a new snapshot of your bootfs will be created on every kernel update. If you leave bootfs.snapshot set persistently on your kernel command line, you may find the below script helpful for automatically removing old snapshots of the bootfs along with their associated kernel.
#!/usr/bin/sh
if [[ "$1" == "remove" ]] && grep -q "\bbootfs.snapshot\b" /proc/cmdline; then
zfs destroy $(findmnt -n -o source /)@$2 &> /dev/null
fi
exit 0
To use the above script place it in a plain text file named /etc/kernel/install.d/99-zfs-cleanup.install and mark it executable with the following command:
$ chmod +x /etc/kernel/install.d/99-zfs-cleanup.install
On Red Hat based systems, you can change the value of `installonly_limit` in /etc/dnf/dnf.conf to adjust the number of kernels and their associated snapshots that are kept.
* `bootfs.snapshot=<snapname>`: Is identical to the bootfs.snapshot parameter explained above except that the value substituted for \<snapname\> will be used when creating the snapshot instead of the version string of the kernel currently being booted.
* `bootfs.rollback`: If listed, enables the zfs-rollback-bootfs service on a Dracut system. The zfs-rollback-bootfs service simply runs `zfs rollback -Rf $BOOTFS@%v` after the pool has been imported but before the bootfs is mounted. If the rollback operation fails, the boot process will be interrupted with a Dracut rescue shell. __Use this parameter with caution. Intermediate snapshots of the bootfs will be destroyed!__ TIP: Keep your user data (e.g. /home) on separate file systems (it can be in the same pool though).
* `bootfs.rollback=<snapname>`: Is identical to the bootfs.rollback parameter explained above except that the value substituted for \<snapname\> will be used when rolling back the bootfs instead of the version string of the kernel currently being booted. If you use this form, choose a snapshot that is new enough to contain the needed kernel modules under /lib/modules or use a kernel that has all the needed modules built-in.
How it Works
============