1#!/bin/sh
2
3command -v getarg >/dev/null || . /lib/dracut-lib.sh
4command -v getargbool >/dev/null || {
5    # Compatibility with older Dracut versions.
6    # With apologies to the Dracut developers.
7    getargbool() {
8        _default="$1"; shift
9        ! _b=$(getarg "$@") && [ -z "$_b" ] && _b="$_default"
10        if [ -n "$_b" ]; then
11            [ "$_b" = "0" ] && return 1
12            [ "$_b" = "no" ] && return 1
13            [ "$_b" = "off" ] && return 1
14        fi
15        return 0
16    }
17}
18
19OLDIFS="${IFS}"
20NEWLINE="
21"
22TAB="	"
23
24ZPOOL_IMPORT_OPTS=""
25if getargbool 0 zfs_force -y zfs.force -y zfsforce ; then
26    warn "ZFS: Will force-import pools if necessary."
27    ZPOOL_IMPORT_OPTS="${ZPOOL_IMPORT_OPTS} -f"
28fi
29
30# find_bootfs
31#   returns the first dataset with the bootfs attribute.
32find_bootfs() {
33    IFS="${NEWLINE}"
34    for dataset in $(zpool list -H -o bootfs); do
35        case "${dataset}" in
36            "" | "-")
37                continue
38                ;;
39            "no pools available")
40                IFS="${OLDIFS}"
41                return 1
42                ;;
43            *)
44                IFS="${OLDIFS}"
45                echo "${dataset}"
46                return 0
47                ;;
48        esac
49    done
50
51    IFS="${OLDIFS}"
52    return 1
53}
54
55# import_pool POOL
56#   imports the given zfs pool if it isn't imported already.
57import_pool() {
58    pool="${1}"
59
60    if ! zpool list -H "${pool}" > /dev/null 2>&1; then
61        info "ZFS: Importing pool ${pool}..."
62        # shellcheck disable=SC2086
63        if ! zpool import -N ${ZPOOL_IMPORT_OPTS} "${pool}" ; then
64            warn "ZFS: Unable to import pool ${pool}"
65            return 1
66        fi
67    fi
68
69    return 0
70}
71
72_mount_dataset_cb() {
73    mount -o zfsutil -t zfs "${1}" "${NEWROOT}${2}"
74}
75
76# mount_dataset DATASET
77#   mounts the given zfs dataset.
78mount_dataset() {
79    dataset="${1}"
80    mountpoint="$(zfs get -H -o value mountpoint "${dataset}")"
81    ret=0
82
83    # We need zfsutil for non-legacy mounts and not for legacy mounts.
84    if [ "${mountpoint}" = "legacy" ] ; then
85        mount -t zfs "${dataset}" "${NEWROOT}" || ret=$?
86    else
87        mount -o zfsutil -t zfs "${dataset}" "${NEWROOT}" || ret=$?
88
89        if [ "$ret" = "0" ]; then
90            for_relevant_root_children "${dataset}" _mount_dataset_cb || ret=$?
91        fi
92    fi
93
94    return ${ret}
95}
96
97# for_relevant_root_children DATASET EXEC
98#   Runs "EXEC dataset mountpoint" for all children of DATASET that are needed for system bringup
99#   Used by zfs-generator.sh and friends, too!
100for_relevant_root_children() {
101    dataset="${1}"
102    exec="${2}"
103
104    zfs list -t filesystem -Ho name,mountpoint,canmount -r "${dataset}" |
105        (
106            _ret=0
107            while IFS="${TAB}" read -r dataset mountpoint canmount; do
108                [ "$canmount" != "on" ] && continue
109
110                case "$mountpoint" in
111                    /etc|/bin|/lib|/lib??|/libx32|/usr)
112                        # 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
113                        "${exec}" "${dataset}" "${mountpoint}" || _ret=$?
114                        ;;
115                    *)
116                        # Up to the real init to remount everything else it might need
117                        ;;
118                esac
119            done
120            exit ${_ret}
121        )
122}
123
124# export_all OPTS
125#   exports all imported zfs pools.
126export_all() {
127    ret=0
128
129    IFS="${NEWLINE}"
130    for pool in $(zpool list -H -o name) ; do
131        if zpool list -H "${pool}" > /dev/null 2>&1; then
132            zpool export "${pool}" "$@" || ret=$?
133        fi
134    done
135    IFS="${OLDIFS}"
136
137    return ${ret}
138}
139
140# ask_for_password
141#
142# Wraps around plymouth ask-for-password and adds fallback to tty password ask
143# if plymouth is not present.
144#
145# --cmd command
146#   Command to execute. Required.
147# --prompt prompt
148#   Password prompt. Note that function already adds ':' at the end.
149#   Recommended.
150# --tries n
151#   How many times repeat command on its failure.  Default is 3.
152# --ply-[cmd|prompt|tries]
153#   Command/prompt/tries specific for plymouth password ask only.
154# --tty-[cmd|prompt|tries]
155#   Command/prompt/tries specific for tty password ask only.
156# --tty-echo-off
157#   Turn off input echo before tty command is executed and turn on after.
158#   It's useful when password is read from stdin.
159ask_for_password() {
160    ply_tries=3
161    tty_tries=3
162    while [ "$#" -gt 0 ]; do
163        case "$1" in
164            --cmd) ply_cmd="$2"; tty_cmd="$2"; shift;;
165            --ply-cmd) ply_cmd="$2"; shift;;
166            --tty-cmd) tty_cmd="$2"; shift;;
167            --prompt) ply_prompt="$2"; tty_prompt="$2"; shift;;
168            --ply-prompt) ply_prompt="$2"; shift;;
169            --tty-prompt) tty_prompt="$2"; shift;;
170            --tries) ply_tries="$2"; tty_tries="$2"; shift;;
171            --ply-tries) ply_tries="$2"; shift;;
172            --tty-tries) tty_tries="$2"; shift;;
173            --tty-echo-off) tty_echo_off=yes;;
174        esac
175        shift
176    done
177
178    { flock -s 9;
179        # Prompt for password with plymouth, if installed and running.
180        if plymouth --ping 2>/dev/null; then
181            plymouth ask-for-password \
182                --prompt "$ply_prompt" --number-of-tries="$ply_tries" | \
183                eval "$ply_cmd"
184            ret=$?
185        else
186            if [ "$tty_echo_off" = yes ]; then
187                stty_orig="$(stty -g)"
188                stty -echo
189            fi
190
191            i=1
192            while [ "$i" -le "$tty_tries" ]; do
193                [ -n "$tty_prompt" ] && \
194                    printf "%s [%i/%i]:" "$tty_prompt" "$i" "$tty_tries" >&2
195                eval "$tty_cmd" && ret=0 && break
196                ret=$?
197                i=$((i+1))
198                [ -n "$tty_prompt" ] && printf '\n' >&2
199            done
200            unset i
201            [ "$tty_echo_off" = yes ] && stty "$stty_orig"
202        fi
203    } 9>/.console_lock
204
205    [ $ret -ne 0 ] && echo "Wrong password" >&2
206    return $ret
207}
208