Skip to content

Commit f78f723

Browse files
committed
zfsbootmenu: manage efivarfs when needed
initcpio mounts efivarfs as read-write, dracut doesn't mount it at all. Inside zfsbootmenu-init, we now check if the system is in EFI mode and then mount or remount efivarfs as read-only. When chrooting into a boot environment, efivarfs is mounted in the chroot as read-only if the zpool is read-only, and read-write if the zpool is read-write. When entering a recovery shell, efivarfs is re-mounted as read-write if efibootmgr is present in the initramfs. Exiting the shell remounts it as read-only.
1 parent c0cee74 commit f78f723

File tree

8 files changed

+128
-10
lines changed

8 files changed

+128
-10
lines changed

docs/online/recovery-shell.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Common Commands
2828

2929
Mount the filesystem at a unique location and print the mount point.
3030

31+
**mount_efivarfs** *mode*
32+
33+
Mount or remount *efivarfs* as read-write or read-only.
34+
3135
**help**
3236

3337
View the online help system.

zfsbootmenu/bin/zfs-chroot

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ cleanup() {
88
umount "${_fs}" || zerror "unable to unmount ${_fs}"
99
done
1010

11+
mount_efivarfs
12+
1113
_mnt=()
1214
trap - HUP INT QUIT ABRT EXIT
1315
}
@@ -37,6 +39,19 @@ if ! mountpoint="$( allow_rw=yes mount_zfs "${selected}" )"; then
3739
exit 1
3840
fi
3941

42+
pool="${selected%%/*}"
43+
44+
# Snapshots and read-only pools always produce read-only mounts
45+
if is_snapshot "${selected}" || ! is_writable "${pool}"; then
46+
writemode="$( colorize green "read-only")"
47+
efivarmode="ro"
48+
else
49+
writemode="$( colorize red "read/write")"
50+
efivarmode="rw"
51+
fi
52+
53+
notices+=( "* $( colorize orange "${selected}" ) is mounted ${writemode}" )
54+
4055
# Track submounts so we know how to clean up on exit
4156
trap cleanup HUP INT QUIT ABRT EXIT
4257
_mnt=( "${mountpoint}" )
@@ -55,20 +70,20 @@ mount -t sysfs sys "${mountpoint}/sys" \
5570
mount -B /dev "${mountpoint}/dev" \
5671
&& _mnt=( "${mountpoint}/dev" "${_mnt[@]}" )
5772

73+
74+
if mount_efivarfs "${efivarmode}" ; then
75+
efivarfs="${mountpoint}/sys/firmware/efi/efivars"
76+
mount_efivarfs "${efivarmode}" "${efivarfs}" \
77+
&& _mnt=( "${efivarfs}" "${_mnt[@]}" )
78+
79+
notices+=( "\n* $( colorize orange "efivarfs" ) is mounted ${writemode}" )
80+
fi
81+
5882
# Not all /dev filesystems have /dev/pts
5983
[ -d "${mountpoint}/dev/pts" ] \
6084
&& mount -t devpts pts "${mountpoint}/dev/pts" \
6185
&& _mnt=( "${mountpoint}/dev/pts" "${_mnt[@]}" )
6286

63-
pool="${selected%%/*}"
64-
65-
# Snapshots and read-only pools always produce read-only mounts
66-
if is_snapshot "${selected}" || ! is_writable "${pool}"; then
67-
writemode="$( colorize green "read-only")"
68-
else
69-
writemode="$( colorize red "read/write")"
70-
fi
71-
7287
_SHELL=
7388
if [ -x "${mountpoint}/bin/bash" ] \
7489
&& chroot "${mountpoint}" /bin/bash -c "exit 0" >/dev/null 2>&1 ; then
@@ -88,7 +103,8 @@ if [ -z "${_SHELL}" ]; then
88103
exit 1
89104
fi
90105

91-
echo -e "$( colorize orange "${selected}") is mounted ${writemode}, /tmp is shared and read/write\n"
106+
notices+=( "\n* $( colorize orange "/tmp" ) is mounted $( colorize red "read/write")" )
107+
echo -e "${notices[*]}\n"
92108

93109
# regardless of shell, set PS1
94110
# shellcheck disable=SC2086

zfsbootmenu/help-files/132/recovery-shell.ansi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828

2929
Mount the filesystem at a unique location and print the mount point.
3030

31+
mount_efivarfs mode
32+
33+
Mount or remount efivarfs as read-write or read-only.
34+
3135
help
3236

3337
View the online help system.

zfsbootmenu/help-files/52/recovery-shell.ansi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
Mount the filesystem at a unique location and
3434
print the mount point.
3535

36+
mount_efivarfs mode
37+
38+
Mount or remount efivarfs as read-write or
39+
read-only.
40+
3641
help
3742

3843
View the online help system.

zfsbootmenu/help-files/92/recovery-shell.ansi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828

2929
Mount the filesystem at a unique location and print the mount point.
3030

31+
mount_efivarfs mode
32+
33+
Mount or remount efivarfs as read-write or read-only.
34+
3135
help
3236

3337
View the online help system.

zfsbootmenu/lib/zfsbootmenu-completions.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,14 @@ _zkexec() {
101101

102102
}
103103
complete -F _zkexec zkexec
104+
105+
_mount_efivarfs() {
106+
local STATE
107+
COMPREPLY=()
108+
109+
[ "${#COMP_WORDS[@]}" != "2" ] && return
110+
111+
STATE=("ro" "rw")
112+
COMPREPLY=( $( compgen -W "${STATE[*]}" -- "${COMP_WORDS[1]}" ) )
113+
}
114+
complete -F _mount_efivarfs mount_efivarfs

zfsbootmenu/lib/zfsbootmenu-core.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,9 @@ emergency_shell() {
17951795
type '$( colorize red "exit")' to return to ZFSBootMenu
17961796
17971797
EOF
1798+
1799+
command -v efibootmgr >/dev/null 2>&1 && mount_efivarfs "rw"
1800+
17981801
# -i (interactive) mode will source $HOME/.bashrc
17991802
/bin/bash -i
18001803

@@ -1805,6 +1808,9 @@ emergency_shell() {
18051808
zdebug "unmounting: ${mp}"
18061809
fi
18071810
done < /proc/self/mounts
1811+
1812+
# always remount as read-only
1813+
mount_efivarfs
18081814
}
18091815

18101816
# prints: zpool list and zfs property list
@@ -1876,3 +1882,68 @@ import_zbm_hooks() {
18761882

18771883
return 0
18781884
}
1885+
1886+
# arg1: directory path
1887+
# prints: nothing
1888+
# returns: 0 if the directory is a mountpoint, 1 if directory is not a mountpoint
1889+
1890+
is_mountpoint() {
1891+
local mount_path dev path opts
1892+
1893+
if command -v mountpoint >/dev/null 2>&1; then
1894+
mountpoint -q "${1}"
1895+
return
1896+
fi
1897+
1898+
if ! mount_path="$(readlink -f "${1}")"; then
1899+
zerror "parent of ${1} does not exist"
1900+
return 1
1901+
fi
1902+
1903+
if [ ! -r /proc/self/mounts ]; then
1904+
zerror "unable to read mount database"
1905+
return 1
1906+
fi
1907+
1908+
# shellcheck disable=SC2034
1909+
while read -r dev path opts; do
1910+
path="$(readlink -f "${path}")" || continue
1911+
[ "${path}" = "${mount_path}" ] && return 0
1912+
done < /proc/self/mounts
1913+
1914+
return 1
1915+
}
1916+
1917+
# args: none
1918+
# prints: nothing
1919+
# returns: 0 if EFI is detected, 1 if not
1920+
1921+
is_efi_system() {
1922+
[ -d /sys/firmware/efi ] && return 0
1923+
return 1
1924+
}
1925+
1926+
1927+
# arg1: 'ro' or 'rw' to mount or remount efivarfs in that desired mode
1928+
# arg2: optional mountpoint
1929+
# prints: nothing
1930+
# returns: 0 on success, 1 on failure, 2 if unsupported
1931+
1932+
mount_efivarfs() {
1933+
local efivar_state efivar_location
1934+
1935+
efivar_state="${1:-ro}"
1936+
efivar_location="${2:-/sys/firmware/efi/efivars}"
1937+
1938+
if ! is_efi_system ; then
1939+
zdebug "efivarfs unsupported"
1940+
return 2
1941+
elif is_mountpoint "${efivar_location}" >/dev/null 2>&1 ; then
1942+
zdebug "remounting '${efivar_location}' '${efivar_state}'"
1943+
# remounting is cheap enough that it's not worth detecting the current state
1944+
mount -t efivarfs efivarfs "${efivar_location}" -o "remount,${efivar_state}"
1945+
else
1946+
zdebug "mounting '${efivar_location}' '${efivar_state}'"
1947+
mount -t efivarfs efivarfs "${efivar_location}" -o "${efivar_state}"
1948+
fi
1949+
}

zfsbootmenu/libexec/zfsbootmenu-init

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ unset src sources
2626

2727
mkdir -p "${BASE:=/zfsbootmenu}"
2828

29+
# explicitly mount efivarfs as read-only
30+
mount_efivarfs "ro"
31+
2932
# Attempt to load spl normally
3033
if ! _modload="$( modprobe spl 2>&1 )" ; then
3134
zdebug "${_modload}"

0 commit comments

Comments
 (0)