contrib; dracut: centralise root= parsing, actually support root=s

So far, everything parsed root= manually, which meant that while
zfs-parse.sh was updated, and supposedly supported + -> ' ' conversion,
it meant nothing

Instead, centralise parsing, and allow:
  root=
  root=zfs
  root=zfs:
  root=zfs:AUTO

  root=ZFS=data/set
  root=zfs:data/set
  root=zfs:ZFS=data/set (as a side-effect; allowed but undocumented)

  rootfstype=zfs AND root=data/set <=> root=data/set
  rootfstype=zfs AND root=         <=> root=zfs:AUTO

So rootfstype=zfs /also/ behaves as expected, and + decoding works

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Closes #13291
This commit is contained in:
наб 2022-04-04 22:45:58 +02:00 committed by Brian Behlendorf
parent 2c74617bcf
commit 245529d85f
6 changed files with 107 additions and 118 deletions

View File

@ -3,34 +3,20 @@
. /lib/dracut-zfs-lib.sh . /lib/dracut-zfs-lib.sh
ZFS_DATASET="" decode_root_args || return 0
ZFS_POOL=""
case "${root}" in
zfs:*) ;;
*) return ;;
esac
GENERATOR_FILE=/run/systemd/generator/sysroot.mount GENERATOR_FILE=/run/systemd/generator/sysroot.mount
GENERATOR_EXTENSION=/run/systemd/generator/sysroot.mount.d/zfs-enhancement.conf GENERATOR_EXTENSION=/run/systemd/generator/sysroot.mount.d/zfs-enhancement.conf
if [ -e "$GENERATOR_FILE" ] && [ -e "$GENERATOR_EXTENSION" ] ; then if [ -e "$GENERATOR_FILE" ] && [ -e "$GENERATOR_EXTENSION" ]; then
# If the ZFS sysroot.mount flag exists, the initial RAM disk configured # We're under systemd and dracut-zfs-generator ran to completion.
# it to mount ZFS on root. In that case, we bail early. This flag info "ZFS: Delegating root mount to sysroot.mount at al."
# file gets created by the zfs-generator program upon successful run.
info "ZFS: There is a sysroot.mount and zfs-generator has extended it."
info "ZFS: Delegating root mount to sysroot.mount."
# Let us tell the initrd to run on shutdown.
# We have a shutdown hook to run
# because we imported the pool.
# We now prevent Dracut from running this thing again. # We now prevent Dracut from running this thing again.
for zfsmounthook in "$hookdir"/mount/*zfs* ; do rm -f "$hookdir"/mount/*zfs*
if [ -f "$zfsmounthook" ] ; then
rm -f "$zfsmounthook"
fi
done
return return
fi fi
info "ZFS: No sysroot.mount exists or zfs-generator did not extend it." info "ZFS: No sysroot.mount exists or zfs-generator did not extend it."
info "ZFS: Mounting root with the traditional mount-zfs.sh instead." info "ZFS: Mounting root with the traditional mount-zfs.sh instead."
@ -38,6 +24,9 @@ info "ZFS: Mounting root with the traditional mount-zfs.sh instead."
modprobe zfs 2>/dev/null modprobe zfs 2>/dev/null
udevadm settle udevadm settle
ZFS_DATASET=
ZFS_POOL=
if [ "${root}" = "zfs:AUTO" ] ; then if [ "${root}" = "zfs:AUTO" ] ; then
if ! ZFS_DATASET="$(find_bootfs)" ; then if ! ZFS_DATASET="$(find_bootfs)" ; then
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@ -53,7 +42,7 @@ if [ "${root}" = "zfs:AUTO" ] ; then
info "ZFS: Using ${ZFS_DATASET} as root." info "ZFS: Using ${ZFS_DATASET} as root."
fi fi
ZFS_DATASET="${ZFS_DATASET:-${root#zfs:}}" ZFS_DATASET="${ZFS_DATASET:-${root}}"
ZFS_POOL="${ZFS_DATASET%%/*}" ZFS_POOL="${ZFS_DATASET%%/*}"
if import_pool "${ZFS_POOL}" ; then if import_pool "${ZFS_POOL}" ; then

View File

@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
# shellcheck disable=SC2034,SC2154 # shellcheck disable=SC2034,SC2154
. /lib/dracut-lib.sh # shellcheck source=zfs-lib.sh.in
. /lib/dracut-zfs-lib.sh
# Let the command line override our host id. # Let the command line override our host id.
spl_hostid=$(getarg spl_hostid=) spl_hostid=$(getarg spl_hostid=)
@ -15,46 +16,20 @@ else
warn "ZFS: Pools may not import correctly." warn "ZFS: Pools may not import correctly."
fi fi
wait_for_zfs=0 if decode_root_args; then
case "${root}" in if [ "$root" = "zfs:AUTO" ]; then
""|zfs|zfs:) info "ZFS: Boot dataset autodetected from bootfs=."
# We'll take root unset, root=zfs, or root=zfs: else
# No root set, so we want to read the bootfs attribute. We info "ZFS: Boot dataset is ${root}."
# can't do that until udev settles so we'll set dummy values fi
# and hope for the best later on.
root="zfs:AUTO"
rootok=1 rootok=1
wait_for_zfs=1 # Make sure Dracut is happy that we have a root and will wait for ZFS
# modules to settle before mounting.
info "ZFS: Enabling autodetection of bootfs after udev settles." if [ -n "${wait_for_zfs}" ]; then
;; ln -s null /dev/root
echo '[ -e /dev/zfs ]' > "${hookdir}/initqueue/finished/zfs.sh"
ZFS=*|zfs:*) fi
# root is explicit ZFS root. Parse it now. We can handle else
# a root=... param in any of the following formats: info "ZFS: no ZFS-on-root."
# root=ZFS=rpool/ROOT
# root=zfs:rpool/ROOT
# root=ZFS=pool+with+space/ROOT+WITH+SPACE (translates to root=ZFS=pool with space/ROOT WITH SPACE)
# Strip down to just the pool/fs
root="${root#zfs:}"
root="zfs:${root#ZFS=}"
# switch + with spaces because kernel cmdline does not allow us to quote parameters
root=$(echo "$root" | tr '+' ' ')
rootok=1
wait_for_zfs=1
info "ZFS: Set ${root} as bootfs."
;;
*)
info "ZFS: no ZFS-on-root"
esac
# Make sure Dracut is happy that we have a root and will wait for ZFS
# modules to settle before mounting.
if [ "${wait_for_zfs}" -eq 1 ]; then
ln -s /dev/null /dev/root 2>/dev/null
initqueuedir="${hookdir}/initqueue/finished"
echo '[ -e /dev/zfs ]' > "${initqueuedir}/zfs.sh"
fi fi

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# shellcheck disable=SC2016,SC1004 # shellcheck disable=SC2016,SC1004,SC2154
grep -wq debug /proc/cmdline && debug=1 grep -wq debug /proc/cmdline && debug=1
[ -n "$debug" ] && echo "zfs-generator: starting" >> /dev/kmsg [ -n "$debug" ] && echo "zfs-generator: starting" >> /dev/kmsg
@ -10,37 +10,17 @@ GENERATOR_DIR="$1"
exit 1 exit 1
} }
[ -f /lib/dracut-lib.sh ] && dracutlib=/lib/dracut-lib.sh # shellcheck source=zfs-lib.sh.in
[ -f /usr/lib/dracut/modules.d/99base/dracut-lib.sh ] && dracutlib=/usr/lib/dracut/modules.d/99base/dracut-lib.sh
command -v getarg >/dev/null 2>&1 || {
[ -n "$debug" ] && echo "zfs-generator: loading Dracut library from $dracutlib" >> /dev/kmsg
. "$dracutlib"
}
. /lib/dracut-zfs-lib.sh . /lib/dracut-zfs-lib.sh
decode_root_args || exit 0
[ -z "$root" ] && root=$(getarg root=) [ -z "${rootflags}" ] && rootflags=$(getarg rootflags=)
[ -z "$rootfstype" ] && rootfstype=$(getarg rootfstype=)
[ -z "$rootflags" ] && rootflags=$(getarg rootflags=)
# If root is not ZFS= or zfs: or rootfstype is not zfs
# then we are not supposed to handle it.
[ "${root##zfs:}" = "${root}" ] &&
[ "${root##ZFS=}" = "${root}" ] &&
[ "$rootfstype" != "zfs" ] &&
exit 0
case ",${rootflags}," in case ",${rootflags}," in
*,zfsutil,*) ;; *,zfsutil,*) ;;
,,) rootflags=zfsutil ;; ,,) rootflags=zfsutil ;;
*) rootflags="zfsutil,${rootflags}" ;; *) rootflags="zfsutil,${rootflags}" ;;
esac esac
if [ "${root}" != "zfs:AUTO" ]; then
root="${root##zfs:}"
root="${root##ZFS=}"
fi
[ -n "$debug" ] && echo "zfs-generator: writing extension for sysroot.mount to $GENERATOR_DIR/sysroot.mount.d/zfs-enhancement.conf" >> /dev/kmsg [ -n "$debug" ] && echo "zfs-generator: writing extension for sysroot.mount to $GENERATOR_DIR/sysroot.mount.d/zfs-enhancement.conf" >> /dev/kmsg
@ -89,7 +69,7 @@ else
_zfs_generator_cb() { _zfs_generator_cb() {
dset="${1}" dset="${1}"
mpnt="${2}" mpnt="${2}"
unit="sysroot$(echo "$mpnt" | tr '/' '-').mount" unit="$(systemd-escape --suffix=mount -p "/sysroot${mpnt}")"
{ {
echo "[Unit]" echo "[Unit]"

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
command -v getarg >/dev/null || . /lib/dracut-lib.sh command -v getarg >/dev/null || . /lib/dracut-lib.sh || . /usr/lib/dracut/modules.d/99base/dracut-lib.sh
command -v getargbool >/dev/null || { command -v getargbool >/dev/null || {
# Compatibility with older Dracut versions. # Compatibility with older Dracut versions.
# With apologies to the Dracut developers. # With apologies to the Dracut developers.
@ -161,7 +161,9 @@ ask_for_password() {
shift shift
done done
{ flock -s 9; {
flock -s 9
# Prompt for password with plymouth, if installed and running. # Prompt for password with plymouth, if installed and running.
if plymouth --ping 2>/dev/null; then if plymouth --ping 2>/dev/null; then
plymouth ask-for-password \ plymouth ask-for-password \
@ -191,3 +193,58 @@ ask_for_password() {
[ "$ret" -ne 0 ] && echo "Wrong password" >&2 [ "$ret" -ne 0 ] && echo "Wrong password" >&2
return "$ret" return "$ret"
} }
# Parse root=, rootfstype=, return them decoded and normalised to zfs:AUTO for auto, plain dset for explicit
#
# True if ZFS-on-root, false if we shouldn't
#
# Supported values:
# root=
# root=zfs
# root=zfs:
# root=zfs:AUTO
#
# root=ZFS=data/set
# root=zfs:data/set
# root=zfs:ZFS=data/set (as a side-effect; allowed but undocumented)
#
# rootfstype=zfs AND root=data/set <=> root=data/set
# rootfstype=zfs AND root= <=> root=zfs:AUTO
#
# '+'es in explicit dataset decoded to ' 's.
decode_root_args() {
if [ -n "$rootfstype" ]; then
[ "$rootfstype" = zfs ]
return
fi
root=$(getarg root=)
rootfstype=$(getarg rootfstype=)
# shellcheck disable=SC2249
case "$root" in
""|zfs|zfs:|zfs:AUTO)
root=zfs:AUTO
rootfstype=zfs
return 0
;;
ZFS=*|zfs:*)
root="${root#zfs:}"
root="${root#ZFS=}"
root=$(echo "$root" | tr '+' ' ')
rootfstype=zfs
return 0
;;
esac
if [ "$rootfstype" = "zfs" ]; then
case "$root" in
"") root=zfs:AUTO ;;
*) root=$(echo "$root" | tr '+' ' ') ;;
esac
return 0
fi
return 1
}

View File

@ -4,32 +4,20 @@
# only run this on systemd systems, we handle the decrypt in mount-zfs.sh in the mount hook otherwise # only run this on systemd systems, we handle the decrypt in mount-zfs.sh in the mount hook otherwise
[ -e /bin/systemctl ] || [ -e /usr/bin/systemctl ] || return 0 [ -e /bin/systemctl ] || [ -e /usr/bin/systemctl ] || return 0
# This script only gets executed on systemd systems, see mount-zfs.sh for non-systemd systems # shellcheck source=zfs-lib.sh.in
. /lib/dracut-zfs-lib.sh
# import the libs now that we know the pool imported decode_root_args || return 0
[ -f /lib/dracut-lib.sh ] && dracutlib=/lib/dracut-lib.sh
[ -f /usr/lib/dracut/modules.d/99base/dracut-lib.sh ] && dracutlib=/usr/lib/dracut/modules.d/99base/dracut-lib.sh
# shellcheck source=./lib-zfs.sh.in
. "$dracutlib"
# load the kernel command line vars
[ -z "$root" ] && root="$(getarg root=)"
# If root is not ZFS= or zfs: or rootfstype is not zfs then we are not supposed to handle it.
[ "${root##zfs:}" = "${root}" ] && [ "${root##ZFS=}" = "${root}" ] && [ "$rootfstype" != "zfs" ] && exit 0
# There is a race between the zpool import and the pre-mount hooks, so we wait for a pool to be imported # There is a race between the zpool import and the pre-mount hooks, so we wait for a pool to be imported
while [ "$(zpool list -H)" = "" ]; do while ! systemctl is-active --quiet zfs-import.target; do
systemctl is-failed --quiet zfs-import-cache.service zfs-import-scan.service && exit 1 systemctl is-failed --quiet zfs-import-cache.service zfs-import-scan.service && return 1
sleep 0.1s sleep 0.1s
done done
# run this after import as zfs-import-cache/scan service is confirmed good BOOTFS="$root"
# we do not overwrite the ${root} variable, but create a new one, BOOTFS, to hold the dataset if [ "$BOOTFS" = "zfs:AUTO" ]; then
if [ "${root}" = "zfs:AUTO" ] ; then BOOTFS="$(zpool get -Ho value bootfs | grep -m1 -vFx -)"
BOOTFS="$(zpool list -H -o bootfs | awk '$1 != "-" {print; exit}')"
else
BOOTFS="${root##zfs:}"
BOOTFS="${BOOTFS##ZFS=}"
fi fi
# if pool encryption is active and the zfs command understands '-o encryption' # if pool encryption is active and the zfs command understands '-o encryption'

View File

@ -16,18 +16,18 @@ Encrypted datasets have keys loaded automatically or prompted for.
If the root dataset contains children with `mountpoint=`s of `/etc`, `/bin`, `/lib*`, or `/usr`, they're mounted too. If the root dataset contains children with `mountpoint=`s of `/etc`, `/bin`, `/lib*`, or `/usr`, they're mounted too.
## cmdline ## cmdline
1. `root=` | Root dataset is… | Pools imported | 1. `root=` | Root dataset is… |
-------------------|----------------------------------------------------------|----------------| ---------------------------|----------------------------------------------------------|
*(empty)* | the first `bootfs=` after `zpool import -aN` | all | *(empty)* | the first `bootfs=` after `zpool import -aN` |
`zfs:AUTO` | *(as above, but overriding other autoselection methods)* | all | `zfs:AUTO`, `zfs:`, `zfs` | *(as above, but overriding other autoselection methods)* |
`ZFS=pool/dataset` | `pool/dataset` | `pool` | `ZFS=pool/dataset` | `pool/dataset` |
`zfs:pool/dataset` | *(as above)* | `pool` | `zfs:pool/dataset` | *(as above)* |
All `+`es are replaced with spaces (i.e. to boot from `root pool/data set`, pass `root=zfs:root+pool/data+set`). All `+`es are replaced with spaces (i.e. to boot from `root pool/data set`, pass `root=zfs:root+pool/data+set`).
The dataset can be at any depth, including being the pool's root dataset (i.e. `root=zfs:pool`). The dataset can be at any depth, including being the pool's root dataset (i.e. `root=zfs:pool`).
`rootfstype=zfs` is mostly equivalent to `root=zfs:AUTO`. `rootfstype=zfs` is equivalent to `root=zfs:AUTO`, `rootfstype=zfs root=pool/dataset` is equivalent to `root=zfs:pool/dataset`.
2. `spl_hostid`: passed to `zgenhostid -f`, useful to override the `/etc/hostid` file baked into the initrd. 2. `spl_hostid`: passed to `zgenhostid -f`, useful to override the `/etc/hostid` file baked into the initrd.