aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/dracut/90zfs/zfs-lib.sh.in
blob: 10b0b701a233ed196aa00114d193cd23999e9e32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/bin/sh

command -v getarg >/dev/null || . /lib/dracut-lib.sh
command -v getargbool >/dev/null || {
    # Compatibility with older Dracut versions.
    # With apologies to the Dracut developers.
    getargbool() {
        _default="$1"; shift
        ! _b=$(getarg "$@") && [ -z "$_b" ] && _b="$_default"
        if [ -n "$_b" ]; then
            [ "$_b" = "0" ] && return 1
            [ "$_b" = "no" ] && return 1
            [ "$_b" = "off" ] && return 1
        fi
        return 0
    }
}

OLDIFS="${IFS}"
NEWLINE="
"
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="${ZPOOL_IMPORT_OPTS} -f"
fi

# find_bootfs
#   returns the first dataset with the bootfs attribute.
find_bootfs() {
    IFS="${NEWLINE}"
    for dataset in $(zpool list -H -o bootfs); do
        case "${dataset}" in
            "" | "-")
                continue
                ;;
            "no pools available")
                IFS="${OLDIFS}"
                return 1
                ;;
            *)
                IFS="${OLDIFS}"
                echo "${dataset}"
                return 0
                ;;
        esac
    done

    IFS="${OLDIFS}"
    return 1
}

# import_pool POOL
#   imports the given zfs pool if it isn't imported already.
import_pool() {
    pool="${1}"

    if ! zpool list -H "${pool}" > /dev/null 2>&1; then
        info "ZFS: Importing pool ${pool}..."
        # shellcheck disable=SC2086
        if ! zpool import -N ${ZPOOL_IMPORT_OPTS} "${pool}" ; then
            warn "ZFS: Unable to import pool ${pool}"
            return 1
        fi
    fi

    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}"
    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-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() {
    ret=0

    IFS="${NEWLINE}"
    for pool in $(zpool list -H -o name) ; do
        if zpool list -H "${pool}" > /dev/null 2>&1; then
            zpool export "${pool}" "$@" || ret=$?
        fi
    done
    IFS="${OLDIFS}"

    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() {
    ply_tries=3
    tty_tries=3
    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 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

            i=1
            while [ "$i" -le "$tty_tries" ]; do
                [ -n "$tty_prompt" ] && \
                    printf "%s [%i/%i]:" "$tty_prompt" "$i" "$tty_tries" >&2
                eval "$tty_cmd" && ret=0 && break
                ret=$?
                i=$((i+1))
                [ -n "$tty_prompt" ] && printf '\n' >&2
            done
            unset i
            [ "$tty_echo_off" = yes ] && stty "$stty_orig"
        fi
    } 9>/.console_lock

    [ $ret -ne 0 ] && echo "Wrong password" >&2
    return $ret
}