diff --git a/contrib/dracut/90zfs/mount-zfs.sh.in b/contrib/dracut/90zfs/mount-zfs.sh.in index 36f07d667b..5b6998b74a 100755 --- a/contrib/dracut/90zfs/mount-zfs.sh.in +++ b/contrib/dracut/90zfs/mount-zfs.sh.in @@ -59,28 +59,13 @@ if import_pool "${ZFS_POOL}" ; then # Load keys if we can or if we need to if [ $(zpool list -H -o feature@encryption $(echo "${ZFS_POOL}" | awk -F\/ '{print $1}')) == 'active' ]; then # if the root dataset has encryption enabled - if $(zfs list -H -o encryption "${ZFS_DATASET}" | grep -q -v off); then - # figure out where the root dataset has its key, the keylocation should not be none - while true; do - if [[ $(zfs list -H -o keylocation "${ZFS_DATASET}") == 'none' ]]; then - ZFS_DATASET=$(echo -n "${ZFS_DATASET}" | awk 'BEGIN{FS=OFS="/"}{NF--; print}') - if [[ "${ZFS_DATASET}" == '' ]]; then - rootok=0 - break - fi - else - rootok=1 - break - fi - done - [[ "${rootok}" -eq 0 ]]&& return 1 + ENCRYPTIONROOT=$(zfs get -H -o value encryptionroot ${ZFS_DATASET}) + if ! [ "${ENCRYPTIONROOT}" = "-" ]; then # decrypt them - TRY_COUNT=5 - while [ $TRY_COUNT != 0 ]; do - zfs load-key "${ZFS_DATASET}" - [ $? == 0 ] && break - ((TRY_COUNT-=1)) - done + ask_for_password \ + --tries 5 \ + --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}: " \ + --cmd "zfs load-key ${ENCRYPTIONROOT}" fi fi # Let us tell the initrd to run on shutdown. diff --git a/contrib/dracut/90zfs/zfs-lib.sh.in b/contrib/dracut/90zfs/zfs-lib.sh.in index c988dfe606..82666afd05 100755 --- a/contrib/dracut/90zfs/zfs-lib.sh.in +++ b/contrib/dracut/90zfs/zfs-lib.sh.in @@ -103,3 +103,75 @@ export_all() { return ${ret} } + +# ask_for_password +# +# Wraps around plymouth ask-for-password and adds fallback to tty password ask +# if plymouth is not present. +# +# --cmd command +# Command to execute. Required. +# --prompt prompt +# Password prompt. Note that function already adds ':' at the end. +# Recommended. +# --tries n +# How many times repeat command on its failure. Default is 3. +# --ply-[cmd|prompt|tries] +# Command/prompt/tries specific for plymouth password ask only. +# --tty-[cmd|prompt|tries] +# Command/prompt/tries specific for tty password ask only. +# --tty-echo-off +# Turn off input echo before tty command is executed and turn on after. +# It's useful when password is read from stdin. +ask_for_password() { + local cmd; local prompt; local tries=3 + local ply_cmd; local ply_prompt; local ply_tries=3 + local tty_cmd; local tty_prompt; local tty_tries=3 + local ret + + while [ $# -gt 0 ]; do + case "$1" in + --cmd) ply_cmd="$2"; tty_cmd="$2"; shift;; + --ply-cmd) ply_cmd="$2"; shift;; + --tty-cmd) tty_cmd="$2"; shift;; + --prompt) ply_prompt="$2"; tty_prompt="$2"; shift;; + --ply-prompt) ply_prompt="$2"; shift;; + --tty-prompt) tty_prompt="$2"; shift;; + --tries) ply_tries="$2"; tty_tries="$2"; shift;; + --ply-tries) ply_tries="$2"; shift;; + --tty-tries) tty_tries="$2"; shift;; + --tty-echo-off) tty_echo_off=yes;; + esac + shift + done + + { flock -s 9; + # Prompt for password with plymouth, if installed and running. + if type plymouth >/dev/null 2>&1 && plymouth --ping 2>/dev/null; then + plymouth ask-for-password \ + --prompt "$ply_prompt" --number-of-tries=$ply_tries \ + --command="$ply_cmd" + ret=$? + else + if [ "$tty_echo_off" = yes ]; then + stty_orig="$(stty -g)" + stty -echo + fi + + local i=1 + while [ $i -le $tty_tries ]; do + [ -n "$tty_prompt" ] && \ + printf "$tty_prompt [$i/$tty_tries]:" >&2 + eval "$tty_cmd" && ret=0 && break + ret=$? + i=$(($i+1)) + [ -n "$tty_prompt" ] && printf '\n' >&2 + done + + [ "$tty_echo_off" = yes ] && stty $stty_orig + fi + } 9>/.console_lock + + [ $ret -ne 0 ] && echo "Wrong password" >&2 + return $ret +} diff --git a/contrib/dracut/90zfs/zfs-load-key.sh.in b/contrib/dracut/90zfs/zfs-load-key.sh.in index 9a6241bd76..6c1f423aea 100755 --- a/contrib/dracut/90zfs/zfs-load-key.sh.in +++ b/contrib/dracut/90zfs/zfs-load-key.sh.in @@ -33,21 +33,13 @@ fi # if pool encryption is active and the zfs command understands '-o encryption' if [[ $(zpool list -H -o feature@encryption $(echo "${root}" | awk -F\/ '{print $1}')) == 'active' ]]; then - # check if root dataset has encryption enabled - if $(zfs list -H -o encryption "${root}" | grep -q -v off); then - # figure out where the root dataset has its key, the keylocation should not be none - while true; do - if [[ $(zfs list -H -o keylocation "${root}") == 'none' ]]; then - root=$(echo -n "${root}" | awk 'BEGIN{FS=OFS="/"}{NF--; print}') - [[ "${root}" == '' ]] && exit 1 - else - break - fi - done + # if the root dataset has encryption enabled + ENCRYPTIONROOT=$(zfs get -H -o value encryptionroot ${ZFS_DATASET}) + if ! [ "${ENCRYPTIONROOT}" = "-" ]; then # decrypt them TRY_COUNT=5 while [ $TRY_COUNT != 0 ]; do - zfs load-key "$root" <<< $(systemd-ask-password "Encrypted ZFS password for ${root}: ") + zfs load-key "${ENCRYPTIONROOT}" <<< $(systemd-ask-password "Encrypted ZFS password for ${root}: ") [[ $? == 0 ]] && break ((TRY_COUNT-=1)) done