From 9052e3d70bacdbcc51cbef8b7f0566b49d2f736b Mon Sep 17 00:00:00 2001 From: gregory-lee-bartholomew Date: Fri, 29 May 2020 23:16:08 -0500 Subject: [PATCH] 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 Reviewed-by: Brian Behlendorf Signed-off-by: Gregory Bartholomew Closes #10198 --- contrib/dracut/90zfs/.gitignore | 2 ++ contrib/dracut/90zfs/Makefile.am | 8 +++++-- contrib/dracut/90zfs/module-setup.sh.in | 6 +++++ .../90zfs/zfs-rollback-bootfs.service.in | 14 +++++++++++ .../90zfs/zfs-snapshot-bootfs.service.in | 14 +++++++++++ contrib/dracut/README.dracut.markdown | 24 +++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 contrib/dracut/90zfs/zfs-rollback-bootfs.service.in create mode 100644 contrib/dracut/90zfs/zfs-snapshot-bootfs.service.in diff --git a/contrib/dracut/90zfs/.gitignore b/contrib/dracut/90zfs/.gitignore index 85c23f75ec..dce2439347 100644 --- a/contrib/dracut/90zfs/.gitignore +++ b/contrib/dracut/90zfs/.gitignore @@ -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 diff --git a/contrib/dracut/90zfs/Makefile.am b/contrib/dracut/90zfs/Makefile.am index 1680230fa3..08667a134c 100644 --- a/contrib/dracut/90zfs/Makefile.am +++ b/contrib/dracut/90zfs/Makefile.am @@ -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' \ diff --git a/contrib/dracut/90zfs/module-setup.sh.in b/contrib/dracut/90zfs/module-setup.sh.in index e94f9f2dc9..7e7a96d6e0 100755 --- a/contrib/dracut/90zfs/module-setup.sh.in +++ b/contrib/dracut/90zfs/module-setup.sh.in @@ -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 } diff --git a/contrib/dracut/90zfs/zfs-rollback-bootfs.service.in b/contrib/dracut/90zfs/zfs-rollback-bootfs.service.in new file mode 100644 index 0000000000..4b058c1b8c --- /dev/null +++ b/contrib/dracut/90zfs/zfs-rollback-bootfs.service.in @@ -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 diff --git a/contrib/dracut/90zfs/zfs-snapshot-bootfs.service.in b/contrib/dracut/90zfs/zfs-snapshot-bootfs.service.in new file mode 100644 index 0000000000..cfd5f7029f --- /dev/null +++ b/contrib/dracut/90zfs/zfs-snapshot-bootfs.service.in @@ -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 diff --git a/contrib/dracut/README.dracut.markdown b/contrib/dracut/README.dracut.markdown index b5fb288a13..f31543c3cf 100644 --- a/contrib/dracut/README.dracut.markdown +++ b/contrib/dracut/README.dracut.markdown @@ -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=`: Is identical to the bootfs.snapshot parameter explained above except that the value substituted for \ 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=`: Is identical to the bootfs.rollback parameter explained above except that the value substituted for \ 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 ============