mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-27 19:39:35 +03:00
3399a30ee0
This had always worked in my testing, but a user on hardware reported this to happen 100%, and I reproduced it once with cold VM host caches. dracut-zfs-generator runs as a systemd generator, i.e. at Some Relatively Early Time; if root= is a fixed dataset, it tries to "solve [necessities] statically at generation time". If by that point zfs-import.target hasn't popped (because the import is taking a non-negligible amount of time for whatever reason), it'll see no children for the root datase, and as such generate no mounts. This has never had any right to work. No-one caught this earlier because it's just that much more convenient to have root=zfs:AUTO, which orders itself properly. To fix this, always run zfs-nonroot-necessities.service; this additionally simplifies the implementation by: * making BOOTFS from zfs-env-bootfs.service be the real, canonical, root dataset name, not just "whatever the first bootfs is", and only set it if we're ZFS-booting * zfs-{rollback,snapshot}-bootfs.service can use this instead of re-implementing it * having zfs-env-bootfs.service also set BOOTFSFLAGS * this means the sysroot.mount drop-in can be fixed text * zfs-nonroot-necessities.service can also be constant and always enabled, because it's conditioned on BOOTFS being set There is no longer any code generated at run-time (the sysroot.mount drop-in is an unavoidable gratuitous cp). The flow of BOOTFS{,FLAGS} from zfs-env-bootfs.service to sysroot.mount is not noted explicitly in dracut.zfs(7), because (a) at some point it's just visual noise and (b) it's already ordered via d-p-m.s from z-i.t. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz> Closes #14690
121 lines
3.2 KiB
Bash
Executable File
121 lines
3.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# shellcheck disable=SC2034
|
|
|
|
command -v getarg >/dev/null || . /lib/dracut-lib.sh || . /usr/lib/dracut/modules.d/99base/dracut-lib.sh
|
|
|
|
TAB=" "
|
|
|
|
ZPOOL_IMPORT_OPTS=
|
|
if getargbool 0 zfs_force -y zfs.force -y zfsforce; then
|
|
warn "ZFS: Will force-import pools if necessary."
|
|
ZPOOL_IMPORT_OPTS=-f
|
|
fi
|
|
|
|
_mount_dataset_cb() {
|
|
# shellcheck disable=SC2154
|
|
mount -o zfsutil -t zfs "${1}" "${NEWROOT}${2}"
|
|
}
|
|
|
|
# mount_dataset DATASET
|
|
# mounts the given zfs dataset.
|
|
mount_dataset() {
|
|
dataset="${1}"
|
|
mountpoint="$(zfs get -H -o value mountpoint "${dataset}")"
|
|
ret=0
|
|
|
|
# We need zfsutil for non-legacy mounts and not for legacy mounts.
|
|
if [ "${mountpoint}" = "legacy" ] ; then
|
|
mount -t zfs "${dataset}" "${NEWROOT}" || ret=$?
|
|
else
|
|
mount -o zfsutil -t zfs "${dataset}" "${NEWROOT}" || ret=$?
|
|
|
|
if [ "$ret" = "0" ]; then
|
|
for_relevant_root_children "${dataset}" _mount_dataset_cb || ret=$?
|
|
fi
|
|
fi
|
|
|
|
return "${ret}"
|
|
}
|
|
|
|
# for_relevant_root_children DATASET EXEC
|
|
# Runs "EXEC dataset mountpoint" for all children of DATASET that are needed for system bringup
|
|
# Used by zfs-nonroot-necessities.service and friends, too!
|
|
for_relevant_root_children() {
|
|
dataset="${1}"
|
|
exec="${2}"
|
|
|
|
zfs list -t filesystem -Ho name,mountpoint,canmount -r "${dataset}" |
|
|
(
|
|
_ret=0
|
|
while IFS="${TAB}" read -r dataset mountpoint canmount; do
|
|
[ "$canmount" != "on" ] && continue
|
|
|
|
case "$mountpoint" in
|
|
/etc|/bin|/lib|/lib??|/libx32|/usr)
|
|
# If these aren't mounted we may not be able to get to the real init at all, or pollute the dataset holding the rootfs
|
|
"${exec}" "${dataset}" "${mountpoint}" || _ret=$?
|
|
;;
|
|
*)
|
|
# Up to the real init to remount everything else it might need
|
|
;;
|
|
esac
|
|
done
|
|
exit "${_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
|
|
|
|
xroot=$(getarg root=)
|
|
rootfstype=$(getarg rootfstype=)
|
|
|
|
# shellcheck disable=SC2249
|
|
case "$xroot" in
|
|
""|zfs|zfs:|zfs:AUTO)
|
|
root=zfs:AUTO
|
|
rootfstype=zfs
|
|
return 0
|
|
;;
|
|
|
|
ZFS=*|zfs:*)
|
|
root="${xroot#zfs:}"
|
|
root="${root#ZFS=}"
|
|
root=$(echo "$root" | tr '+' ' ')
|
|
rootfstype=zfs
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
if [ "$rootfstype" = "zfs" ]; then
|
|
case "$xroot" in
|
|
"") root=zfs:AUTO ;;
|
|
*) root=$(echo "$xroot" | tr '+' ' ') ;;
|
|
esac
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|