mirror_zfs/etc/systemd/system-generators/zfs-mount-generator.in
Witaut Bajaryn 618206c0b9 Skip loading already loaded key
Don't ask for the password / try to load the key if the key for the
encryptionroot is already loaded.  The user might have loaded the key
manually or by other means before the scripts get called.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tom Caputi <tcaputi@datto.com>
Reviewed-by: Richard Laager <rlaager@wiktel.com>
Signed-off-by: Witaut Bajaryn <vitaut.bayaryn@gmail.com>
Closes #9495
Closes #9529
2020-01-22 13:49:03 -08:00

258 lines
7.1 KiB
Bash
Executable File

#!/bin/sh
# zfs-mount-generator - generates systemd mount units for zfs
# Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
set -e
FSLIST="@sysconfdir@/zfs/zfs-list.cache"
[ -d "${FSLIST}" ] || exit 0
do_fail() {
printf 'zfs-mount-generator: %s\n' "$*" > /dev/kmsg
exit 1
}
# see systemd.generator
if [ $# -eq 0 ] ; then
dest_norm="/tmp"
elif [ $# -eq 3 ] ; then
dest_norm="${1}"
else
do_fail "zero or three arguments required"
fi
# For ZFSs marked "auto", a dependency is created for local-fs.target. To
# avoid regressions, this dependency is reduced to "wants" rather than
# "requires". **THIS MAY CHANGE**
req_dir="${dest_norm}/local-fs.target.wants/"
mkdir -p "${req_dir}"
# All needed information about each ZFS is available from
# zfs list -H -t filesystem -o <properties>
# cached in $FSLIST, and each line is processed by the following function:
# See the list below for the properties and their order
process_line() {
# zfs list -H -o name,...
# fields are tab separated
IFS="$(printf '\t')"
# protect against special characters in, e.g., mountpoints
set -f
set -- $1
dataset="${1}"
p_mountpoint="${2}"
p_canmount="${3}"
p_atime="${4}"
p_relatime="${5}"
p_devices="${6}"
p_exec="${7}"
p_readonly="${8}"
p_setuid="${9}"
p_nbmand="${10}"
p_encroot="${11}"
p_keyloc="${12}"
# Check for canmount=off .
if [ "${p_canmount}" = "off" ] ; then
return
elif [ "${p_canmount}" = "noauto" ] ; then
# Don't let a noauto marked mountpoint block an "auto" market mountpoint
return
elif [ "${p_canmount}" = "on" ] ; then
: # This is OK
else
do_fail "invalid canmount"
fi
# Check for legacy and blank mountpoints.
if [ "${p_mountpoint}" = "legacy" ] ; then
return
elif [ "${p_mountpoint}" = "none" ] ; then
return
elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
do_fail "invalid mountpoint $*"
fi
# Escape the mountpoint per systemd policy.
mountfile="$(systemd-escape "${p_mountpoint#?}").mount"
# Parse options
# see lib/libzfs/libzfs_mount.c:zfs_add_options
opts=""
# atime
if [ "${p_atime}" = on ] ; then
# relatime
if [ "${p_relatime}" = on ] ; then
opts="${opts},atime,relatime"
elif [ "${p_relatime}" = off ] ; then
opts="${opts},atime,strictatime"
else
printf 'zfs-mount-generator: (%s) invalid relatime\n' \
"${dataset}" >/dev/kmsg
fi
elif [ "${p_atime}" = off ] ; then
opts="${opts},noatime"
else
printf 'zfs-mount-generator: (%s) invalid atime\n' \
"${dataset}" >/dev/kmsg
fi
# devices
if [ "${p_devices}" = on ] ; then
opts="${opts},dev"
elif [ "${p_devices}" = off ] ; then
opts="${opts},nodev"
else
printf 'zfs-mount-generator: (%s) invalid devices\n' \
"${dataset}" >/dev/kmsg
fi
# exec
if [ "${p_exec}" = on ] ; then
opts="${opts},exec"
elif [ "${p_exec}" = off ] ; then
opts="${opts},noexec"
else
printf 'zfs-mount-generator: (%s) invalid exec\n' \
"${dataset}" >/dev/kmsg
fi
# readonly
if [ "${p_readonly}" = on ] ; then
opts="${opts},ro"
elif [ "${p_readonly}" = off ] ; then
opts="${opts},rw"
else
printf 'zfs-mount-generator: (%s) invalid readonly\n' \
"${dataset}" >/dev/kmsg
fi
# setuid
if [ "${p_setuid}" = on ] ; then
opts="${opts},suid"
elif [ "${p_setuid}" = off ] ; then
opts="${opts},nosuid"
else
printf 'zfs-mount-generator: (%s) invalid setuid\n' \
"${dataset}" >/dev/kmsg
fi
# nbmand
if [ "${p_nbmand}" = on ] ; then
opts="${opts},mand"
elif [ "${p_nbmand}" = off ] ; then
opts="${opts},nomand"
else
printf 'zfs-mount-generator: (%s) invalid nbmand\n' \
"${dataset}" >/dev/kmsg
fi
# Minimal pre-requisites to mount a ZFS dataset
wants="zfs-import.target"
if [ -n "${p_encroot}" ] &&
[ "${p_encroot}" != "-" ] ; then
keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
if [ "${p_encroot}" = "${dataset}" ] ; then
pathdep=""
if [ "${p_keyloc%%://*}" = "file" ] ; then
pathdep="RequiresMountsFor='${p_keyloc#file://}'"
keyloadcmd="@sbindir@/zfs load-key '${dataset}'"
elif [ "${p_keyloc}" = "prompt" ] ; then
keyloadcmd="sh -c 'set -eu;"\
"keystatus=\"\$\$(@sbindir@/zfs get -H -o value keystatus \"${dataset}\")\";"\
"[ \"\$\$keystatus\" = \"unavailable\" ] || exit 0;"\
"count=0;"\
"while [ \$\$count -lt 3 ];do"\
" systemd-ask-password --id=\"zfs:${dataset}\""\
" \"Enter passphrase for ${dataset}:\"|"\
" @sbindir@/zfs load-key \"${dataset}\" && exit 0;"\
" count=\$\$((count + 1));"\
"done;"\
"exit 1'"
else
printf 'zfs-mount-generator: (%s) invalid keylocation\n' \
"${dataset}" >/dev/kmsg
fi
cat > "${dest_norm}/${keyloadunit}" << EOF
# Automatically generated by zfs-mount-generator
[Unit]
Description=Load ZFS key for ${dataset}
SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
DefaultDependencies=no
Wants=${wants}
After=${wants}
${pathdep}
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=${keyloadcmd}
ExecStop=@sbindir@/zfs unload-key '${dataset}'
EOF
fi
# Update the dependencies for the mount file to require the
# key-loading unit.
wants="${wants} ${keyloadunit}"
fi
# If the mountpoint has already been created, give it precedence.
if [ -e "${dest_norm}/${mountfile}" ] ; then
printf 'zfs-mount-generator: %s already exists\n' "${mountfile}" \
>/dev/kmsg
return
fi
# By ordering before zfs-mount.service, we avoid race conditions.
cat > "${dest_norm}/${mountfile}" << EOF
# Automatically generated by zfs-mount-generator
[Unit]
SourcePath=${cachefile}
Documentation=man:zfs-mount-generator(8)
Before=local-fs.target zfs-mount.service
After=${wants}
Wants=${wants}
[Mount]
Where=${p_mountpoint}
What=${dataset}
Type=zfs
Options=defaults${opts},zfsutil
EOF
# Finally, create the appropriate dependency
ln -s "../${mountfile}" "${req_dir}"
}
# Feed each line into process_line
for cachefile in "${FSLIST}/"* ; do
while read -r fs ; do
process_line "${fs}"
done < "${cachefile}"
done