mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
Systemd mount generator: Generate noauto units; add control properties
This commit refactors the systemd mount generators and makes the
following major changes:
- The generator now generates units for datasets marked canmount=noauto,
too. These units are NOT WantedBy local-fs.target.
If there are multiple noauto datasets for a path, no noauto unit will
be created. Datasets with canmount=on are prioritized.
- Introduces handling of new user properties which are now included in
the zfs-list.cache files:
- org.openzfs.systemd:requires:
List of units to require for this mount unit
- org.openzfs.systemd:requires-mounts-for:
List of mounts to require by this mount unit
- org.openzfs.systemd:before:
List of units to order after this mount unit
- org.openzfs.systemd:after:
List of units to order before this mount unit
- org.openzfs.systemd:wanted-by:
List of units to add a Wants dependency on this mount unit to
- org.openzfs.systemd:required-by:
List of units to add a Requires dependency on this mount unit to
- org.openzfs.systemd:nofail:
Toggles between a wants and a requires dependency.
- org.openzfs.systemd:ignore:
Do not generate a mount unit for this dataset.
Consult the updated man page for detailed documentation.
- Restructures and extends the zfs-mount-generator(8) man page with the
above properties, information on unit ordering and a license header.
Reviewed-by: Richard Laager <rlaager@wiktel.com>
Reviewed-by: Antonio Russo <antonio.e.russo@gmail.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: InsanePrawn <insane.prawny@gmail.com>
Closes #9649
This commit is contained in:
committed by
Brian Behlendorf
parent
9d2f3b7f94
commit
ecbbdac799
@@ -2,6 +2,7 @@
|
||||
|
||||
# zfs-mount-generator - generates systemd mount units for zfs
|
||||
# Copyright (c) 2017 Antonio Russo <antonio.e.russo@gmail.com>
|
||||
# Copyright (c) 2020 InsanePrawn <insane.prawny@gmail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -33,6 +34,35 @@ do_fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# test if $1 is in space-separated list $2
|
||||
is_known() {
|
||||
query="$1"
|
||||
IFS=' '
|
||||
# protect against special characters
|
||||
set -f
|
||||
for element in $2 ; do
|
||||
if [ "$query" = "$element" ] ; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# create dependency on unit file $1
|
||||
# of type $2, i.e. "wants" or "requires"
|
||||
# in the target units from space-separated list $3
|
||||
create_dependencies() {
|
||||
unitfile="$1"
|
||||
suffix="$2"
|
||||
# protect against special characters
|
||||
set -f
|
||||
for target in $3 ; do
|
||||
target_dir="${dest_norm}/${target}.${suffix}/"
|
||||
mkdir -p "${target_dir}"
|
||||
ln -s "../${unitfile}" "${target_dir}"
|
||||
done
|
||||
}
|
||||
|
||||
# see systemd.generator
|
||||
if [ $# -eq 0 ] ; then
|
||||
dest_norm="/tmp"
|
||||
@@ -42,11 +72,6 @@ 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>
|
||||
@@ -74,18 +99,58 @@ process_line() {
|
||||
p_nbmand="${10}"
|
||||
p_encroot="${11}"
|
||||
p_keyloc="${12}"
|
||||
p_systemd_requires="${13}"
|
||||
p_systemd_requiresmountsfor="${14}"
|
||||
p_systemd_before="${15}"
|
||||
p_systemd_after="${16}"
|
||||
p_systemd_wantedby="${17}"
|
||||
p_systemd_requiredby="${18}"
|
||||
p_systemd_nofail="${19}"
|
||||
p_systemd_ignore="${20}"
|
||||
|
||||
# Minimal pre-requisites to mount a ZFS dataset
|
||||
# By ordering before zfs-mount.service, we avoid race conditions.
|
||||
after="zfs-import.target"
|
||||
before="zfs-mount.service"
|
||||
wants="zfs-import.target"
|
||||
requires=""
|
||||
requiredmounts=""
|
||||
wantedby=""
|
||||
requiredby=""
|
||||
noauto="off"
|
||||
|
||||
if [ -n "${p_systemd_after}" ] && \
|
||||
[ "${p_systemd_after}" != "-" ] ; then
|
||||
after="${p_systemd_after} ${after}"
|
||||
fi
|
||||
|
||||
if [ -n "${p_systemd_before}" ] && \
|
||||
[ "${p_systemd_before}" != "-" ] ; then
|
||||
before="${p_systemd_before} ${before}"
|
||||
fi
|
||||
|
||||
if [ -n "${p_systemd_requires}" ] && \
|
||||
[ "${p_systemd_requires}" != "-" ] ; then
|
||||
requires="Requires=${p_systemd_requires}"
|
||||
fi
|
||||
|
||||
if [ -n "${p_systemd_requiresmountsfor}" ] && \
|
||||
[ "${p_systemd_requiresmountsfor}" != "-" ] ; then
|
||||
requiredmounts="RequiresMountsFor=${p_systemd_requiresmountsfor}"
|
||||
fi
|
||||
|
||||
# Handle encryption
|
||||
if [ -n "${p_encroot}" ] &&
|
||||
[ "${p_encroot}" != "-" ] ; then
|
||||
keyloadunit="zfs-load-key-$(systemd-escape "${p_encroot}").service"
|
||||
if [ "${p_encroot}" = "${dataset}" ] ; then
|
||||
pathdep=""
|
||||
keymountdep=""
|
||||
if [ "${p_keyloc%%://*}" = "file" ] ; then
|
||||
pathdep="RequiresMountsFor='${p_keyloc#file://}'"
|
||||
if [ -n "${requiredmounts}" ] ; then
|
||||
keymountdep="${requiredmounts} '${p_keyloc#file://}'"
|
||||
else
|
||||
keymountdep="RequiresMountsFor='${p_keyloc#file://}'"
|
||||
fi
|
||||
keyloadcmd="@sbindir@/zfs load-key '${dataset}'"
|
||||
elif [ "${p_keyloc}" = "prompt" ] ; then
|
||||
keyloadcmd="\
|
||||
@@ -121,8 +186,10 @@ SourcePath=${cachefile}
|
||||
Documentation=man:zfs-mount-generator(8)
|
||||
DefaultDependencies=no
|
||||
Wants=${wants}
|
||||
After=${wants}
|
||||
${pathdep}
|
||||
After=${after}
|
||||
Before=${before}
|
||||
${requires}
|
||||
${keymountdep}
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
@@ -130,23 +197,35 @@ RemainAfterExit=yes
|
||||
ExecStart=${keyloadcmd}
|
||||
ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}"
|
||||
fi
|
||||
# Update the dependencies for the mount file to require the
|
||||
# Update the dependencies for the mount file to want the
|
||||
# key-loading unit.
|
||||
wants="${wants} ${keyloadunit}"
|
||||
after="${after} ${keyloadunit}"
|
||||
fi
|
||||
|
||||
# Prepare the .mount unit
|
||||
|
||||
# skip generation of the mount unit if org.openzfs.systemd:ignore is "on"
|
||||
if [ -n "${p_systemd_ignore}" ] ; then
|
||||
if [ "${p_systemd_ignore}" = "on" ] ; then
|
||||
return
|
||||
elif [ "${p_systemd_ignore}" = "-" ] \
|
||||
|| [ "${p_systemd_ignore}" = "off" ] ; then
|
||||
: # This is OK
|
||||
else
|
||||
do_fail "invalid org.openzfs.systemd:ignore for ${dataset}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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" marked mountpoint
|
||||
return
|
||||
noauto="on"
|
||||
elif [ "${p_canmount}" = "on" ] ; then
|
||||
: # This is OK
|
||||
else
|
||||
do_fail "invalid canmount"
|
||||
do_fail "invalid canmount for ${dataset}"
|
||||
fi
|
||||
|
||||
# Check for legacy and blank mountpoints.
|
||||
@@ -155,7 +234,7 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
|
||||
elif [ "${p_mountpoint}" = "none" ] ; then
|
||||
return
|
||||
elif [ "${p_mountpoint%"${p_mountpoint#?}"}" != "/" ] ; then
|
||||
do_fail "invalid mountpoint $*"
|
||||
do_fail "invalid mountpoint for ${dataset}"
|
||||
fi
|
||||
|
||||
# Escape the mountpoint per systemd policy.
|
||||
@@ -233,15 +312,91 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
|
||||
"${dataset}" >/dev/kmsg
|
||||
fi
|
||||
|
||||
# If the mountpoint has already been created, give it precedence.
|
||||
if [ -n "${p_systemd_wantedby}" ] && \
|
||||
[ "${p_systemd_wantedby}" != "-" ] ; then
|
||||
noauto="on"
|
||||
if [ "${p_systemd_wantedby}" = "none" ] ; then
|
||||
wantedby=""
|
||||
else
|
||||
wantedby="${p_systemd_wantedby}"
|
||||
before="${before} ${wantedby}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${p_systemd_requiredby}" ] && \
|
||||
[ "${p_systemd_requiredby}" != "-" ] ; then
|
||||
noauto="on"
|
||||
if [ "${p_systemd_requiredby}" = "none" ] ; then
|
||||
requiredby=""
|
||||
else
|
||||
requiredby="${p_systemd_requiredby}"
|
||||
before="${before} ${requiredby}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For datasets with canmount=on, a dependency is created for
|
||||
# local-fs.target by default. To avoid regressions, this dependency
|
||||
# is reduced to "wants" rather than "requires" when nofail is not "off".
|
||||
# **THIS MAY CHANGE**
|
||||
# noauto=on disables this behavior completely.
|
||||
if [ "${noauto}" != "on" ] ; then
|
||||
if [ "${p_systemd_nofail}" = "off" ] ; then
|
||||
requiredby="local-fs.target"
|
||||
before="${before} local-fs.target"
|
||||
else
|
||||
wantedby="local-fs.target"
|
||||
if [ "${p_systemd_nofail}" != "on" ] ; then
|
||||
before="${before} local-fs.target"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Handle existing files:
|
||||
# 1. We never overwrite existing files, although we may delete
|
||||
# files if we're sure they were created by us. (see 5.)
|
||||
# 2. We handle files differently based on canmount. Units with canmount=on
|
||||
# always have precedence over noauto. This is enforced by the sort pipe
|
||||
# in the loop around this function.
|
||||
# It is important to use $p_canmount and not $noauto here, since we
|
||||
# sort by canmount while other properties also modify $noauto, e.g.
|
||||
# org.openzfs.systemd:wanted-by.
|
||||
# 3. If no unit file exists for a noauto dataset, we create one.
|
||||
# Additionally, we use $noauto_files to track the unit file names
|
||||
# (which are the systemd-escaped mountpoints) of all (exclusively)
|
||||
# noauto datasets that had a file created.
|
||||
# 4. If the file to be created is found in the tracking variable,
|
||||
# we do NOT create it.
|
||||
# 5. If a file exists for a noauto dataset, we check whether the file
|
||||
# name is in the variable. If it is, we have multiple noauto datasets
|
||||
# for the same mountpoint. In such cases, we remove the file for safety.
|
||||
# To avoid further noauto datasets creating a file for this path again,
|
||||
# we leave the file name in the tracking variable.
|
||||
if [ -e "${dest_norm}/${mountfile}" ] ; then
|
||||
printf 'zfs-mount-generator: %s already exists\n' "${mountfile}" \
|
||||
>/dev/kmsg
|
||||
if is_known "$mountfile" "$noauto_files" ; then
|
||||
# if it's in $noauto_files, we must be noauto too. See 2.
|
||||
printf 'zfs-mount-generator: removing duplicate noauto %s\n' \
|
||||
"${mountfile}" >/dev/kmsg
|
||||
# See 5.
|
||||
rm "${dest_norm}/${mountfile}"
|
||||
else
|
||||
# don't log for canmount=noauto
|
||||
if [ "${p_canmount}" = "on" ] ; then
|
||||
printf 'zfs-mount-generator: %s already exists. Skipping.\n' \
|
||||
"${mountfile}" >/dev/kmsg
|
||||
fi
|
||||
fi
|
||||
# file exists; Skip current dataset.
|
||||
return
|
||||
else
|
||||
if is_known "${mountfile}" "${noauto_files}" ; then
|
||||
# See 4.
|
||||
return
|
||||
elif [ "${p_canmount}" = "noauto" ] ; then
|
||||
noauto_files="${mountfile} ${noauto_files}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create the .mount unit file.
|
||||
# By ordering before zfs-mount.service, we avoid race conditions.
|
||||
#
|
||||
# (Do not use `<<EOF`-style here-documents for this, see warning above)
|
||||
#
|
||||
@@ -251,9 +406,12 @@ ExecStop=@sbindir@/zfs unload-key '${dataset}'" > "${dest_norm}/${keyloadunit}
|
||||
[Unit]
|
||||
SourcePath=${cachefile}
|
||||
Documentation=man:zfs-mount-generator(8)
|
||||
Before=local-fs.target zfs-mount.service
|
||||
After=${wants}
|
||||
|
||||
Before=${before}
|
||||
After=${after}
|
||||
Wants=${wants}
|
||||
${requires}
|
||||
${requiredmounts}
|
||||
|
||||
[Mount]
|
||||
Where=${p_mountpoint}
|
||||
@@ -261,13 +419,20 @@ What=${dataset}
|
||||
Type=zfs
|
||||
Options=defaults${opts},zfsutil" > "${dest_norm}/${mountfile}"
|
||||
|
||||
# Finally, create the appropriate dependency
|
||||
ln -s "../${mountfile}" "${req_dir}"
|
||||
# Finally, create the appropriate dependencies
|
||||
create_dependencies "${mountfile}" "wants" "$wantedby"
|
||||
create_dependencies "${mountfile}" "requires" "$requiredby"
|
||||
|
||||
}
|
||||
|
||||
# Feed each line into process_line
|
||||
for cachefile in "${FSLIST}/"* ; do
|
||||
while read -r fs ; do
|
||||
process_line "${fs}"
|
||||
done < "${cachefile}"
|
||||
# Sort cachefile's lines by canmount, "on" before "noauto"
|
||||
# and feed each line into process_line
|
||||
sort -t "$(printf '\t')" -k 3 -r "${cachefile}" | \
|
||||
( # subshell is necessary for `sort|while read` and $noauto_files
|
||||
noauto_files=""
|
||||
while read -r fs ; do
|
||||
process_line "${fs}"
|
||||
done
|
||||
)
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user