-
Notifications
You must be signed in to change notification settings - Fork 10
/
mount.zfs-non-legacy
executable file
·126 lines (118 loc) · 4.25 KB
/
mount.zfs-non-legacy
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
#!/bin/sh
# mount.zfs-non-legacy (part of ossobv/vcutil) // wdoekes/2021-2022
# // Public Domain
#
# This script is a mount wrapper that adds the 'zfs-non-legacy' type for
# mount(8). It checks whether a particular ZFS dataset is mounted,
# and tries to import the pool and mount it, if it isn't.
#
# Rationale behind this script is the following:
# - systemd has .mount files that can be used in dependencies
# - zfs (non-legacy) doesn't use mount(8), so systemd interfaces poorly with it
# By using this script, we can leverage the power of systemd .mount files
# while using regular ZFS mount points.
#
# Common scenario:
# - /var/lib/docker resides on a separate ZFS pool (data)
# - docker.service needs to depend on that
# If there is no such dependency, docker might start _before_ all zfs imports
# and mounts have completed, data gets written in the root filesystem instead.
#
# >>> Fix in /etc/systemd/system/var-lib-docker.mount <<<
# [Unit]
# Documentation=https://github.com/ossobv/vcutil/blob/main/mount.zfs-non-legacy
# After=zfs-mount.service
# Requires=zfs-mount.service
# [Mount]
# Type=zfs-non-legacy
# What=data/docker
#
# >>> Fix in /etc/systemd/system/docker.service.d/override.conf <<<
# [Unit]
# RequiresMountsFor=/var/lib/docker
#
# Now a start of the docker.service will depend on var-lib-docker.mount.
# That mount unit will call something like this:
# > mount -t zfs-non-legacy data/docker /var/lib/docker
# And that call ends up in this wrapper script:
# - it checks if data is imported, and imports it if it isn't
# - it checks that the mount path is correct
# - it checks whether it is mounted, and if not, it mounts it
#
set -eu
name="$1" # data/docker
path="$2" # /var/lib/docker
if test "${name#/}" != "$name"; then # fileset has a leading slash
# https://github.com/util-linux/util-linux/commit/
# 372ce5b74e79470b1bda1fc284c19a313a422361
# is a fix for fstype = "zfs" only. Here we have
# fstype = "zfs-non-legacy", so it suffers from the same issue as:
# https://github.com/openzfs/zfs/pull/11295#issuecomment-757889868
# > mount -t zfs foo /foo
# > # will run 'mount.zfs /foo /foo' internally because /foo exists
# A satisfactory solution to this problem was if we could tell
# systemd to add the --no-canonicalize mount option:
# https://github.com/systemd/systemd/issues/18188
echo "Looks like systemd/systemd#23795 or systemd/systemd#18188 or\
util-linux/util-linux#1231 is not fixed satisfactorily (we want a\
--no-canonicalize mount option): args=[$*] -- removing slash from\
\$1" >&2
name=${name#/}
fi
if ! command -v zpool >/dev/null || ! command -v zfs >/dev/null; then
echo "No ZFS installed?" >&2
exit 1
fi
# Get mounted state, but tries to import the zpool if it was not found.
# Implemented in a function so we can retry this during boot. During boot
# several jobs may be attempting to mount/import stuff, causing intermittent
# failures that will auto-heal.
get_mounted() {
local name="$1" # data/docker
local zpool=${name%%/*} # data
local found=0
local mounted
mounted="$(zfs list -Homounted "$name")" && found=1
if test $found -eq 0; then
if ! zpool list -Honame "$zpool" >/dev/null; then
if ! zpool import "$zpool"; then
false # intermittent or permanent failure?
return
fi
fi
mounted="$(zfs list -Homounted "$name")" || mounted=no-dataset
fi
echo "$mounted"
}
# Get mounted value. Retry a couple of times.
mounted=
for retry in 1 2 3 0; do
mounted=$(get_mounted "$name") && break
sleep $retry || true
false
done || mounted=no-pool
# Handle the various possibilities.
case $mounted in
no-dataset|no-pool)
echo "zfs-non-legacy: failed to load $name: $mounted" >&2
exit 1
;;
no|yes)
if ! mountpoint=$(zfs list -Homountpoint "$name") ||
test "$path" != "$mountpoint"; then
echo "zfs-non-legacy: dest fail for $name: $path ~ $mountpoint" >&2
exit 1
fi
;;
*)
echo "zfs-non-legacy: unexpected mounted value for $name: $mounted" >&2
exit 1
;;
esac
# So.. it wasn't mounted? Try that then.
if test "$mounted" = no && ! zfs mount "$name"; then
echo "zfs-non-legacy: failed to mount $name" >&2
exit 1
fi
# All good.
exit 0