Skip to content

Commit 951111e

Browse files
zdykstraahesford
andauthored
Add support to discover and assume a hostid, as well as fix arguments passed to a BE (#147)
A persistent problem with ZFS and now with ZFSBootMenu is getting spl.spl_hostid lined up on all pool imports. The contents of an initramfs and the kernel command line can influence this value, making it difficult to determine exactly where and how to change it to get a system to boot. To help combat this recurring issue, ZBM now accepts the following options to control importing behavior. * zbm.import_policy=strict: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, drop to a recovery shell. * zbm.import_policy=hostid: If no pools can be imported with the hostid embedded in the initramfs, or set via spl_hostid on the ZBM command line, attempt to scrape the hostid used to import the pool defined in root=zfsbootmenu:POOL=<value> and import the pool. If that pool is missing or can't be imported, any other available pool is scraped for a hostid and attempted to be imported. * zbm.import_policy=force: YOLO mode, just force import a pool. Replaces `zbm.force_import'. Right now, when undefined, zbm.import_policy is set to strict. With this option available, ZBM can now in most cases import any pool - regardless of the hostid of the system that built the initramfs or the EFI executable. Next is to ensure that the boot environment imports the pool correctly. Some ZFS initramfs modules can use spl_hostid to write out /etc/hostid in the initramfs before loading SPL/ZFS modules. Others ... like Arch ... not only do not by default include /etc/hostid in the initramfs, they do not have any methods to line up spl_hostid on the kernel command line with /etc/hostid. However, spl.spl_hostid takes precedence over /etc/hostid, so we can simply set this on the kernel command line. To this end, we now: Suppress spl_hostid if it's part of the discovered command line for a boot environment Suppress spl.spl_hostid if it's part of the discovered command line for a boot environment Insert spl.spl_hostid=0x<hostid> on the command line for each boot environment. (when hostid is 0, insert spl_hostid=0x00000000, to help dracut. spl.spl_hostid=0 is a no-op). Since we know the hostid used to import a pool - either discovered in zfsbootmenu-parse-commandline.sh, or discovered when zbm.import_policy=hostid is set. Suppression / rewriting can be disabled via zbm.set_hostid=0 when booting ZBM. Co-authored-by: Zach Dykstra <[email protected]> Co-authored-by: Andrew J. Hesford <[email protected]>
1 parent ed9a8c5 commit 951111e

File tree

9 files changed

+448
-123
lines changed

9 files changed

+448
-123
lines changed

90zfsbootmenu/module-setup.sh

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ install() {
8282
"ps"
8383
"env"
8484
"chmod"
85+
"od"
8586
)
8687

8788
for _exec in "${essential_execs[@]}"; do
@@ -202,6 +203,20 @@ install() {
202203
type mark_hostonly >/dev/null 2>&1 && mark_hostonly /etc/zfs/vdev_id.conf
203204
fi
204205

206+
# Determine platform endianness, defaulting to le
207+
ival="$( echo -n 3 | od -tx2 -N2 -An | tr -d '[:space:]' )"
208+
if [ "x${ival}" = "x3300" ]; then
209+
endian="be"
210+
else
211+
if [ "x${ival}" != "x0033" ]; then
212+
warn "unable to determine platform endianness; assuming little-endian"
213+
fi
214+
endian="le"
215+
fi
216+
217+
# shellcheck disable=SC2154
218+
echo -n "${endian}" > "${initdir}/etc/byte-order"
219+
205220
# Try to synchronize hostid between host and ZFSBootMenu
206221
#
207222
# DEPRECATION NOTICE: on musl systems, zfs < 2.0 produced a bad hostid in
@@ -222,7 +237,11 @@ install() {
222237
if [ -z "${NEWZFS}" ]; then
223238
# In zfs < 2.0, zgenhostid does not provide necessary behavior
224239
# shellcheck disable=SC2154
225-
echo -ne "\\x${HOSTID:6:2}\\x${HOSTID:4:2}\\x${HOSTID:2:2}\\x${HOSTID:0:2}" > "${initdir}/etc/hostid"
240+
if [ "${endian}" = "be" ] ; then
241+
echo -ne "\\x${HOSTID:0:2}\\x${HOSTID:2:2}\\x${HOSTID:4:2}\\x${HOSTID:6:2}" > "${initdir}/etc/hostid"
242+
else
243+
echo -ne "\\x${HOSTID:6:2}\\x${HOSTID:4:2}\\x${HOSTID:2:2}\\x${HOSTID:0:2}" > "${initdir}/etc/hostid"
244+
fi
226245
elif [ "${HOSTID}" != "00000000" ]; then
227246
# In zfs >= 2.0, zgenhostid writes the output, but only with nonzero hostid
228247
# shellcheck disable=SC2154

90zfsbootmenu/zfsbootmenu-countdown.sh

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,15 @@ fi
1313

1414
mkdir -p "${BASE}"
1515

16-
# Attempt to import all pools read-only
17-
read_write='' all_pools=yes import_pool
18-
19-
# Make sure at least one pool can be imported; if not,
20-
# drop to an emergency shell to allow the user to attempt recovery
21-
import_success=0
22-
while true; do
23-
while IFS=$'\t' read -r _pool _health; do
24-
[ -n "${_pool}" ] || continue
25-
26-
import_success=1
27-
if [ "${_health}" != "ONLINE" ]; then
28-
echo "${_pool}" >> "${BASE}/degraded"
29-
fi
30-
done <<<"$( zpool list -H -o name,health )"
31-
32-
if [ "${import_success}" -ne 1 ]; then
33-
emergency_shell "unable to successfully import a pool"
34-
else
35-
zdebug "$(
36-
echo "zpool list" ; \
37-
zpool list
38-
)"
39-
zdebug "$(
40-
echo "zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource" ;\
41-
zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource
42-
)"
43-
break
44-
fi
45-
done
16+
# Write out a default or overridden hostid
17+
if [ -n "${spl_hostid}" ] ; then
18+
zinfo "ZFSBootMenu: writing /etc/hostid from command line: ${spl_hostid}"
19+
write_hostid "${spl_hostid}"
20+
elif [ ! -e /etc/hostid ]; then
21+
zinfo "ZFSBootMenu: no hostid found on kernel command line or /etc/hostid"
22+
zinfo "ZFSBootMenu: defaulting hostid to 00000000"
23+
write_hostid 0
24+
fi
4625

4726
# Prefer a specific pool when checking for a bootfs value
4827
# shellcheck disable=SC2154
@@ -52,11 +31,71 @@ else
5231
boot_pool="${root}"
5332
fi
5433

55-
# Make sure the preferred pool was imported
56-
if [ -n "${boot_pool}" ] && ! zpool list -H -o name "${boot_pool}" >/dev/null 2>&1; then
57-
emergency_shell "\nCannot import requested pool '${boot_pool}'\nType 'exit' to try booting anyway"
34+
# Do a dedicated pass for the preferred pool if one was provided
35+
if [ -n "${boot_pool}" ]; then
36+
first_pass=0
37+
else
38+
first_pass=1
5839
fi
5940

41+
while true; do
42+
if [ "${first_pass}" -eq 0 ]; then
43+
# Try the preferred pool, exactly once
44+
zdebug "attempting to import preferred pool ${boot_pool}"
45+
try_pool="${boot_pool}"
46+
else
47+
try_pool=""
48+
fi
49+
50+
first_pass=1
51+
52+
read_write='' import_pool "${try_pool}"
53+
54+
# shellcheck disable=SC2154
55+
if check_for_pools; then
56+
if [ -n "${try_pool}" ]; then
57+
# If a single pool was requested and imported, try again for the others
58+
continue
59+
else
60+
# Otherwise, all possible pools were imported, nothing more to try
61+
break
62+
fi
63+
elif [ "${import_policy}" == "hostid" ] && poolmatch="$( match_hostid "${try_pool}" )"; then
64+
zdebug "match_hostid returned: ${poolmatch}"
65+
66+
spl_hostid="${poolmatch##*;}"
67+
68+
export spl_hostid
69+
70+
# Store the hostid to use for for KCL overrides
71+
echo -n "$spl_hostid" > "${BASE}/spl_hostid"
72+
73+
# Retry the cycle with a matched hostid
74+
continue
75+
fi
76+
77+
# Allow the user to attempt recovery
78+
emergency_shell "unable to successfully import a pool"
79+
done
80+
81+
# restrict read-write access to any unhealthy pools
82+
while IFS=$'\t' read -r _pool _health; do
83+
if [ "${_health}" != "ONLINE" ]; then
84+
echo "${_pool}" >> "${BASE}/degraded"
85+
zerror "prohibiting read/write operations on ${_pool}"
86+
fi
87+
done <<<"$( zpool list -H -o name,health )"
88+
89+
zdebug "$(
90+
echo "zpool list" ; \
91+
zpool list
92+
)"
93+
94+
zdebug "$(
95+
echo "zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource" ;\
96+
zfs list -o name,mountpoint,encroot,keystatus,keylocation,org.zfsbootmenu:keysource
97+
)"
98+
6099
unsupported=0
61100
while IFS=$'\t' read -r _pool _property; do
62101
if [[ "${_property}" =~ "unsupported@" ]]; then

90zfsbootmenu/zfsbootmenu-exec.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#!/bin/bash
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

4+
export endian
45
export spl_hostid
5-
export force_import
6+
export import_policy
67
export menu_timeout
78
export loglevel
89
export root
910
export zbm_sort
11+
export zbm_set_hostid
1012

1113
# Disable all kernel messages to the console
1214
echo 0 > /proc/sys/kernel/printk

0 commit comments

Comments
 (0)