diff --git a/contrib/dracut/90zfs/zfs-generator.sh.in b/contrib/dracut/90zfs/zfs-generator.sh.in index 8e97b896c2..e3fbf334f7 100755 --- a/contrib/dracut/90zfs/zfs-generator.sh.in +++ b/contrib/dracut/90zfs/zfs-generator.sh.in @@ -11,12 +11,13 @@ GENERATOR_DIR="$1" [ -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 - 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 + [ -z "$root" ] && root=$(getarg root=) [ -z "$rootfstype" ] && rootfstype=$(getarg rootfstype=) [ -z "$rootflags" ] && rootflags=$(getarg rootflags=) @@ -28,40 +29,84 @@ command -v getarg >/dev/null 2>&1 || { [ "$rootfstype" != "zfs" ] && exit 0 -rootfstype=zfs case ",${rootflags}," in *,zfsutil,*) ;; ,,) rootflags=zfsutil ;; *) rootflags="zfsutil,${rootflags}" ;; 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 -[ -d "$GENERATOR_DIR" ] || mkdir "$GENERATOR_DIR" -[ -d "$GENERATOR_DIR"/sysroot.mount.d ] || mkdir "$GENERATOR_DIR"/sysroot.mount.d +mkdir -p "$GENERATOR_DIR"/sysroot.mount.d "$GENERATOR_DIR"/initrd-root-fs.target.requires "$GENERATOR_DIR"/dracut-pre-mount.service.d { echo "[Unit]" echo "Before=initrd-root-fs.target" echo "After=zfs-import.target" + echo echo "[Mount]" - if [ "${root}" = "zfs:AUTO" ] ; then + if [ "${root}" = "zfs:AUTO" ]; then echo "PassEnvironment=BOOTFS" echo 'What=${BOOTFS}' else - root="${root##zfs:}" - root="${root##ZFS=}" echo "What=${root}" fi - echo "Type=${rootfstype}" + echo "Type=zfs" echo "Options=${rootflags}" } > "$GENERATOR_DIR"/sysroot.mount.d/zfs-enhancement.conf - -[ -d "$GENERATOR_DIR"/initrd-root-fs.target.requires ] || mkdir -p "$GENERATOR_DIR"/initrd-root-fs.target.requires -ln -s ../sysroot.mount "$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount +ln -fs ../sysroot.mount "$GENERATOR_DIR"/initrd-root-fs.target.requires/sysroot.mount -[ -d "$GENERATOR_DIR"/dracut-pre-mount.service.d ] || mkdir "$GENERATOR_DIR"/dracut-pre-mount.service.d +if [ "${root}" = "zfs:AUTO" ]; then + { + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "After=sysroot.mount" + echo "DefaultDependencies=no" + echo + echo "[Service]" + echo "Type=oneshot" + echo "PassEnvironment=BOOTFS" + echo "ExecStart=/bin/sh -c '" ' \ + . /lib/dracut-zfs-lib.sh; \ + _zfs_nonroot_necessities_cb() { \ + zfs mount | grep -m1 -q "^$1 " && return 0; \ + echo "Mounting $1 on /sysroot$2"; \ + mount -o zfsutil -t zfs "$1" "/sysroot$2"; \ + }; \ + for_relevant_root_children "${BOOTFS}" _zfs_nonroot_necessities_cb;' \ + "'" + } > "$GENERATOR_DIR"/zfs-nonroot-necessities.service + ln -fs ../zfs-nonroot-necessities.service "$GENERATOR_DIR"/initrd-root-fs.target.requires/zfs-nonroot-necessities.service +else + # We can solve this statically at generation time, so do! + _zfs_generator_cb() { + dset="${1}" + mpnt="${2}" + unit="sysroot$(echo "$mpnt" | sed 's;/;-;g').mount" + + { + echo "[Unit]" + echo "Before=initrd-root-fs.target" + echo "After=sysroot.mount" + echo + echo "[Mount]" + echo "Where=/sysroot${mpnt}" + echo "What=${dset}" + echo "Type=zfs" + echo "Options=zfsutil" + } > "$GENERATOR_DIR/${unit}" + ln -fs ../"${unit}" "$GENERATOR_DIR"/initrd-root-fs.target.requires/"${unit}" + } + + for_relevant_root_children "${root}" _zfs_generator_cb +fi + { echo "[Unit]" diff --git a/contrib/dracut/90zfs/zfs-lib.sh.in b/contrib/dracut/90zfs/zfs-lib.sh.in index c39cc5cfff..f3cdd1503d 100755 --- a/contrib/dracut/90zfs/zfs-lib.sh.in +++ b/contrib/dracut/90zfs/zfs-lib.sh.in @@ -23,6 +23,7 @@ command -v getargbool >/dev/null || { OLDIFS="${IFS}" NEWLINE=" " +TAB=" " ZPOOL_IMPORT_OPTS="" if getargbool 0 zfs_force -y zfs.force -y zfsforce ; then @@ -58,7 +59,7 @@ find_bootfs() { # import_pool POOL # imports the given zfs pool if it isn't imported already. import_pool() { - pool="${1}" + pool="${1}" if ! zpool list -H "${pool}" > /dev/null 2>&1; then info "ZFS: Importing pool ${pool}..." @@ -71,26 +72,62 @@ import_pool() { return 0 } +_mount_dataset_cb() { + mount -o zfsutil -t zfs "${1}" "${NEWROOT}${2}" +} + # mount_dataset DATASET # mounts the given zfs dataset. mount_dataset() { - dataset="${1}" + 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}" + mount -t zfs "${dataset}" "${NEWROOT}" || ret=$? else - mount -o zfsutil -t zfs "${dataset}" "${NEWROOT}" + mount -o zfsutil -t zfs "${dataset}" "${NEWROOT}" || ret=$? + + if [ "$ret" = "0" ]; then + for_relevant_root_children "${dataset}" _mount_dataset_cb || ret=$? + fi fi - return $? + 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-generator.sh 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} + ) } # export_all OPTS # exports all imported zfs pools. export_all() { - opts="${@}" + opts="${@}" ret=0 IFS="${NEWLINE}"