]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/units/testsuite-50.sh
docs/RANDOM_SEEDS: update NetBSD link
[thirdparty/systemd.git] / test / units / testsuite-50.sh
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
4 # ex: ts=8 sw=4 sts=4 et filetype=sh
5 # shellcheck disable=SC2233,SC2235
6 set -eux
7 set -o pipefail
8
9 export SYSTEMD_LOG_LEVEL=debug
10
11 cleanup_image_dir() {
12 if [ -z "${image_dir}" ]; then
13 return
14 fi
15 umount "${image_dir}/app0"
16 umount "${image_dir}/app1"
17 umount "${image_dir}/app-nodistro"
18 umount "${image_dir}/service-scoped-test"
19 rm -rf "${image_dir}"
20 }
21
22 fake_roots_dir=/fake-roots
23
24 cleanup_fake_rootfses() {
25 local tries=10 e
26 local -a lines fake_roots_mounts
27
28 while [[ ${tries} -gt 0 ]]; do
29 tries=$((tries - 1))
30 mapfile -t lines < <(mount | awk '{ print $3 }')
31 fake_roots_mounts=()
32 for e in "${lines[@]}"; do
33 if [[ ${e} = "${fake_roots_dir}"/* ]]; then
34 fake_roots_mounts+=( "${e}" )
35 fi
36 done
37 if [[ ${#fake_roots_mounts[@]} -eq 0 ]]; then
38 break
39 fi
40 for e in "${fake_roots_mounts[@]}"; do
41 umount "${e}"
42 done
43 done
44 rm -rf "${fake_roots_dir}"
45 }
46
47 # shellcheck disable=SC2317
48 cleanup() {(
49 set +ex
50
51 cleanup_image_dir
52 cleanup_fake_rootfses
53 )}
54
55 udevadm control --log-level=debug
56
57 cd /tmp
58
59 image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
60 if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
61 echo "mktemp under /tmp failed"
62 exit 1
63 fi
64
65 trap cleanup EXIT
66
67 cp /usr/share/minimal* "${image_dir}/"
68 image="${image_dir}/minimal_0"
69 roothash="$(cat "${image}.roothash")"
70
71 os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
72
73 systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
74 systemd-dissect "${image}.raw" | grep -q -F "MARKER=1"
75 systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release")
76
77 systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$'
78 systemd-dissect --mtree "${image}.raw" --mtree-hash yes | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
79 systemd-dissect --mtree "${image}.raw" --mtree-hash no | grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
80
81 read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "${image}.raw" etc/os-release | sha256sum)
82 test "$SHA256SUM1" != ""
83 read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release)
84 test "$SHA256SUM2" != ""
85 test "$SHA256SUM1" = "$SHA256SUM2"
86
87 if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
88 # Make sure tarballs are reproducible
89 read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
90 test "$SHA256SUM1" != ""
91 read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "${image}.raw" | sha256sum)
92 test "$SHA256SUM2" != ""
93 test "$SHA256SUM1" = "$SHA256SUM2"
94 # Also check that a file we expect to be there is there
95 systemd-dissect --make-archive "${image}.raw" | tar t | grep etc/os-release
96 fi
97
98 mv "${image}.verity" "${image}.fooverity"
99 mv "${image}.roothash" "${image}.foohash"
100 systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
101 systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1"
102 systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release")
103 mv "${image}.fooverity" "${image}.verity"
104 mv "${image}.foohash" "${image}.roothash"
105
106 mkdir -p "${image_dir}/mount" "${image_dir}/mount2"
107 systemd-dissect --mount "${image}.raw" "${image_dir}/mount"
108 grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
109 grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
110 grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
111 # Verity volume should be shared (opened only once)
112 systemd-dissect --mount "${image}.raw" "${image_dir}/mount2"
113 verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
114 # In theory we should check that count is exactly one. In practice, libdevmapper
115 # randomly and unpredictably fails with an unhelpful EINVAL when a device is open
116 # (and even mounted and in use), so best-effort is the most we can do for now
117 if [ "${verity_count}" -lt 1 ]; then
118 echo "Verity device ${image}.raw not found in /dev/mapper/"
119 exit 1
120 fi
121 systemd-dissect --umount "${image_dir}/mount"
122 systemd-dissect --umount "${image_dir}/mount2"
123
124 systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
125 mv "${image}.verity" "${image}.fooverity"
126 mv "${image}.roothash" "${image}.foohash"
127 systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
128 # Let's use the long option name just here as a test
129 systemd-run -P --property RootImage="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
130 mv "${image}.fooverity" "${image}.verity"
131 mv "${image}.foohash" "${image}.roothash"
132
133 # Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
134 machine="$(uname -m)"
135 if [ "${machine}" = "x86_64" ]; then
136 root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
137 verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
138 signature_guid=41092b05-9fc8-4523-994f-2def0408b176
139 architecture="x86-64"
140 elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
141 root_guid=44479540-f297-41b2-9af7-d131d5f0458a
142 verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
143 signature_guid=5996fc05-109c-48de-808b-23fa0830b676
144 architecture="x86"
145 elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
146 root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
147 verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
148 signature_guid=6db69de6-29f4-4758-a7a5-962190f00ce3
149 architecture="arm64"
150 elif [ "${machine}" = "arm" ]; then
151 root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
152 verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
153 signature_guid=42b0455f-eb11-491d-98d3-56145ba9d037
154 architecture="arm"
155 elif [ "${machine}" = "loongarch64" ]; then
156 root_guid=77055800-792c-4f94-b39a-98c91b762bb6
157 verity_guid=f3393b22-e9af-4613-a948-9d3bfbd0c535
158 signature_guid=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
159 architecture="loongarch64"
160 elif [ "${machine}" = "ia64" ]; then
161 root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
162 verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
163 signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a
164 architecture="ia64"
165 elif [ "${machine}" = "s390x" ]; then
166 root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306
167 verity_guid=b325bfbe-c7be-4ab8-8357-139e652d2f6b
168 signature_guid=c80187a5-73a3-491a-901a-017c3fa953e9
169 architecture="s390x"
170 elif [ "${machine}" = "ppc64le" ]; then
171 root_guid=c31c45e6-3f39-412e-80fb-4809c4980599
172 verity_guid=906bd944-4589-4aae-a4e4-dd983917446a
173 signature_guid=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
174 architecture="ppc64-le"
175 else
176 echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
177 exit 1
178 fi
179 # du rounds up to block size, which is more helpful for partitioning
180 root_size="$(du -k "${image}.raw" | cut -f1)"
181 verity_size="$(du -k "${image}.verity" | cut -f1)"
182 signature_size=4
183 # 4MB seems to be the minimum size blkid will accept, below that probing fails
184 dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
185 # sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
186 # so do some basic rounding up if the minimal image is more than 1 MB
187 if [ "${root_size}" -ge 1024 ]; then
188 root_size="$((root_size/1024 + 1))MiB"
189 else
190 root_size="${root_size}KiB"
191 fi
192 verity_size="$((verity_size * 2))KiB"
193 signature_size="$((signature_size * 2))KiB"
194
195 HAVE_OPENSSL=0
196 if systemctl --version | grep -q -- +OPENSSL ; then
197 # The openssl binary is installed conditionally.
198 # If we have OpenSSL support enabled and openssl is missing, fail early
199 # with a proper error message.
200 if ! command -v openssl >/dev/null 2>&1; then
201 echo "openssl missing" >/failed
202 exit 1
203 fi
204
205 HAVE_OPENSSL=1
206 OPENSSL_CONFIG="$(mktemp)"
207 # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
208 cat >"${OPENSSL_CONFIG:?}" <<EOF
209 [ req ]
210 prompt = no
211 distinguished_name = req_distinguished_name
212
213 [ req_distinguished_name ]
214 C = DE
215 ST = Test State
216 L = Test Locality
217 O = Org Name
218 OU = Org Unit Name
219 CN = Common Name
220 emailAddress = test@email.com
221 EOF
222
223 # Create key pair
224 openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
225 # Sign Verity root hash with it
226 openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
227 # Generate signature partition JSON data
228 echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig"
229 # Pad it
230 truncate -s "${signature_size}" "${image}.verity-sig"
231 # Register certificate in the (userspace) verity key ring
232 mkdir -p /run/verity.d
233 ln -s "${image}.crt" /run/verity.d/ok.crt
234 fi
235
236 # Construct a UUID from hash
237 # input: 11111111222233334444555566667777
238 # output: 11111111-2222-3333-4444-555566667777
239 uuid="$(head -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
240 echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk "${image}.gpt"
241 uuid="$(tail -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
242 echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" --append
243 if [ "${HAVE_OPENSSL}" -eq 1 ]; then
244 echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append
245 fi
246 sfdisk --part-label "${image}.gpt" 1 "Root Partition"
247 sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
248 if [ "${HAVE_OPENSSL}" -eq 1 ]; then
249 sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
250 fi
251 loop="$(losetup --show -P -f "${image}.gpt")"
252 partitions=(
253 "${loop:?}p1"
254 "${loop:?}p2"
255 )
256 if [ "${HAVE_OPENSSL}" -eq 1 ]; then
257 partitions+=( "${loop:?}p3" )
258 fi
259 # The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
260 # Let's not expect the devices to be initialized.
261 udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
262 udevadm lock --device="${loop}p1" dd if="${image}.raw" of="${loop}p1"
263 udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2"
264 if [ "${HAVE_OPENSSL}" -eq 1 ]; then
265 udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3"
266 fi
267 losetup -d "${loop}"
268
269 # Derive partition UUIDs from root hash, in UUID syntax
270 ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
271 VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
272
273 systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"signed",'
274 systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,'
275 if [ "${HAVE_OPENSSL}" -eq 1 ]; then
276 systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$architecture"'","verity":null,'
277 fi
278 systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
279 systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")
280
281 # Test image policies
282 systemd-dissect --validate "${image}.gpt"
283 systemd-dissect --validate "${image}.gpt" --image-policy='*'
284 (! systemd-dissect --validate "${image}.gpt" --image-policy='~')
285 (! systemd-dissect --validate "${image}.gpt" --image-policy='-')
286 (! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent)
287 (! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity)
288 systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected
289 systemd-dissect --validate "${image}.gpt" --image-policy=root=verity
290 systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
291 systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent
292 systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected
293 (! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent)
294 systemd-dissect --validate "${image}.gpt" --image-policy=root=signed
295 (! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
296 (! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent)
297
298 # Test RootImagePolicy= unit file setting
299 systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
300 systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
301 (! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
302 (! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
303 (! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
304 systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
305 systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
306 (! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
307
308 systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" "${image_dir}/mount"
309 grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
310 grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
311 grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
312 systemd-dissect --umount "${image_dir}/mount"
313
314 systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" --in-memory "${image_dir}/mount"
315 grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
316 grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
317 grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
318 systemd-dissect --umount "${image_dir}/mount"
319
320 # add explicit -p MountAPIVFS=yes once to test the parser
321 systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
322
323 systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
324 systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
325
326 mkdir -p "${image_dir}/result"
327 cat >/run/systemd/system/testservice-50a.service <<EOF
328 [Service]
329 Type=oneshot
330 ExecStart=bash -c "mount >/run/result/a"
331 BindPaths=${image_dir}/result:/run/result
332 TemporaryFileSystem=/run
333 RootImage=${image}.raw
334 RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
335 RootImageOptions=nosuid,dev
336 EOF
337 systemctl start testservice-50a.service
338 grep -F "squashfs" "${image_dir}/result/a" | grep -q -F "noatime"
339 grep -F "squashfs" "${image_dir}/result/a" | grep -q -F -v "nosuid"
340
341 cat >/run/systemd/system/testservice-50b.service <<EOF
342 [Service]
343 Type=oneshot
344 ExecStart=bash -c "mount >/run/result/b"
345 BindPaths=${image_dir}/result:/run/result
346 TemporaryFileSystem=/run
347 RootImage=${image}.gpt
348 RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
349 RootImageOptions=home:ro,dev nosuid,dev,%%foo
350 # this is the default, but let's specify once to test the parser
351 MountAPIVFS=yes
352 EOF
353 systemctl start testservice-50b.service
354 grep -F "squashfs" "${image_dir}/result/b" | grep -q -F "noatime"
355
356 # Check that specifier escape is applied %%foo → %foo
357 busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
358
359 # Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
360 systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
361 systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
362 systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid"
363 systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
364 systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
365 systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
366 systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1"
367 systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
368 systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
369 cat >/run/systemd/system/testservice-50c.service <<EOF
370 [Service]
371 MountAPIVFS=yes
372 TemporaryFileSystem=/run
373 RootImage=${image}.raw
374 MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
375 MountImages=${image}.raw:/run/img2\:3:nosuid
376 ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
377 ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
378 ExecStart=bash -c "mount >>/run/result/c"
379 BindPaths=${image_dir}/result:/run/result
380 Type=oneshot
381 EOF
382 systemctl start testservice-50c.service
383 grep -q -F "MARKER=1" "${image_dir}/result/c"
384 grep -F "squashfs" "${image_dir}/result/c" | grep -q -F "noatime"
385 grep -F "squashfs" "${image_dir}/result/c" | grep -q -F -v "nosuid"
386
387 # Adding a new mounts at runtime works if the unit is in the active state,
388 # so use Type=notify to make sure there's no race condition in the test
389 cat >/run/systemd/system/testservice-50d.service <<EOF
390 [Service]
391 RuntimeMaxSec=300
392 Type=notify
393 RemainAfterExit=yes
394 MountAPIVFS=yes
395 PrivateTmp=yes
396 ExecStart=sh -c ' \\
397 systemd-notify --ready; \\
398 while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
399 sleep 0.1; \\
400 done; \\
401 mount; \\
402 mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
403 '
404 EOF
405 systemctl start testservice-50d.service
406
407 # Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
408 mkdir -p /tmp/wrong/foo
409 mksquashfs /tmp/wrong/foo /tmp/wrong.raw
410 systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
411 test "$(systemctl show -P SubState testservice-50d.service)" = "running"
412 systemctl mount-image --mkdir testservice-50d.service "${image}.raw" /tmp/img root:nosuid
413
414 while systemctl show -P SubState testservice-50d.service | grep -q running
415 do
416 sleep 0.1
417 done
418
419 systemctl is-active testservice-50d.service
420
421 # ExtensionImages will set up an overlay
422 systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
423 systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
424 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
425 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
426 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
427 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
428 systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
429 systemd-run -P --property ExtensionImages=/etc/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
430 # Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
431 mkdir -p /usr/share/symlink-test/
432 cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
433 ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
434 systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
435
436 # Symlink check again but for confext
437 mkdir -p /etc/symlink-test/
438 cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
439 ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
440 systemd-run -P --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
441 # And again mixing sysext and confext
442 systemd-run -P \
443 --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
444 --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
445 --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
446 systemd-run -P \
447 --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
448 --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
449 --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
450
451 cat >/run/systemd/system/testservice-50e.service <<EOF
452 [Service]
453 MountAPIVFS=yes
454 TemporaryFileSystem=/run /var/lib
455 StateDirectory=app0
456 RootImage=${image}.raw
457 ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
458 # Relevant only for sanitizer runs
459 UnsetEnvironment=LD_PRELOAD
460 ExecStart=bash -c '/opt/script0.sh | grep ID'
461 ExecStart=bash -c '/opt/script1.sh | grep ID'
462 Type=oneshot
463 RemainAfterExit=yes
464 EOF
465 systemctl start testservice-50e.service
466 systemctl is-active testservice-50e.service
467
468 # Check vpick support in ExtensionImages=
469 VBASE="vtest$RANDOM"
470 VDIR="/tmp/${VBASE}.v"
471 mkdir "$VDIR"
472
473 ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
474 ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
475
476 systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
477
478 rm -rf "$VDIR"
479
480 # ExtensionDirectories will set up an overlay
481 mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro" "${image_dir}/service-scoped-test"
482 (! systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh)
483 (! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh)
484 systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
485 systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
486 systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
487 systemd-dissect --mount /etc/service-scoped-test.raw "${image_dir}/service-scoped-test"
488 systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
489 systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
490 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
491 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
492 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
493 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
494 systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
495 systemd-run -P --property ExtensionDirectories="${image_dir}/service-scoped-test" --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
496 cat >/run/systemd/system/testservice-50f.service <<EOF
497 [Service]
498 MountAPIVFS=yes
499 TemporaryFileSystem=/run /var/lib
500 StateDirectory=app0
501 RootImage=${image}.raw
502 ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
503 # Relevant only for sanitizer runs
504 UnsetEnvironment=LD_PRELOAD
505 ExecStart=bash -c '/opt/script0.sh | grep ID'
506 ExecStart=bash -c '/opt/script1.sh | grep ID'
507 Type=oneshot
508 RemainAfterExit=yes
509 EOF
510 systemctl start testservice-50f.service
511 systemctl is-active testservice-50f.service
512
513 # Check vpick support in ExtensionDirectories=
514 VBASE="vtest$RANDOM"
515 VDIR="/tmp/${VBASE}.v"
516 mkdir "$VDIR"
517
518 ln -s "${image_dir}/app0" "$VDIR/${VBASE}_0"
519 ln -s "${image_dir}/app1" "$VDIR/${VBASE}_1"
520
521 systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
522
523 rm -rf "$VDIR"
524
525 systemd-dissect --umount "${image_dir}/app0"
526 systemd-dissect --umount "${image_dir}/app1"
527
528 # Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
529 mkdir -p /var/lib/extensions/
530 ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
531 systemd-sysext merge
532 grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
533 systemd-sysext unmerge
534 mkdir -p /etc/extensions/app-nodistro
535 systemd-sysext merge
536 test ! -e /usr/lib/systemd/system/some_file
537 systemd-sysext unmerge
538 rmdir /etc/extensions/app-nodistro
539
540 # Similar, but go via varlink
541 varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
542 (! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
543 varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
544 grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
545 varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
546 grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
547 varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
548 (! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
549
550 # Check that extensions cannot contain os-release
551 mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
552 echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
553 echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
554 touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
555 (! systemd-sysext merge)
556 test ! -e /usr/lib/systemd/system/some_file
557 test ! -e /usr/lib/systemd/system/other_file
558 systemd-sysext unmerge
559 rm -rf /run/extensions/app-reject
560 rm /var/lib/extensions/app-nodistro.raw
561
562 # Some super basic test that RootImage= works with .v/ dirs
563 VBASE="vtest$RANDOM"
564 VDIR="/tmp/${VBASE}.v"
565 mkdir "$VDIR"
566
567 ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
568 ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
569 ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
570
571 systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
572
573 rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
574 rmdir "$VDIR"
575
576 mkdir -p /run/machines /run/portables /run/extensions
577 touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
578
579 systemd-dissect --discover --json=short >/tmp/discover.json
580 grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
581 grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
582 grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
583 rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
584
585 # Check that the /sbin/mount.ddi helper works
586 T="/tmp/mounthelper.$RANDOM"
587 mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard
588 umount -R "$T"
589 rmdir "$T"
590
591 LOOP="$(systemd-dissect --attach --loop-ref=waldo "${image}.raw")"
592
593 # Wait until the symlinks we want to test are established
594 udevadm trigger -w "$LOOP"
595
596 # Check if the /dev/loop/* symlinks really reference the right device
597 test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
598
599 if [ "$(stat -c '%Hd:%Ld' "${image}.raw")" != '?d:?d' ] ; then
600 # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
601 # instead. Let's simply skip the test on such old systems.
602 test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "${image}.raw")" -ef "$LOOP"
603 fi
604
605 # Detach by loopback device
606 systemd-dissect --detach "$LOOP"
607
608 # Test long reference name.
609 # Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
610 # and --loop-ref accepts upto 63 characters, and udev creates symlink
611 # based on the name when it has upto _62_ characters.
612 name="$(for _ in {1..62}; do echo -n 'x'; done)"
613 LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
614 udevadm trigger -w "$LOOP"
615
616 # Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
617 test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
618
619 # Detach by the /dev/disk/by-loop-ref symlink
620 systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
621
622 name="$(for _ in {1..63}; do echo -n 'x'; done)"
623 LOOP="$(systemd-dissect --attach --loop-ref="$name" "${image}.raw")"
624 udevadm trigger -w "$LOOP"
625
626 # Check if the /dev/disk/by-loop-ref/$name symlink does not exist
627 test ! -e "/dev/disk/by-loop-ref/$name"
628
629 # Detach by backing inode
630 systemd-dissect --detach "${image}.raw"
631 (! systemd-dissect --detach "${image}.raw")
632
633 # check for confext functionality
634 mkdir -p /run/confexts/test/etc/extension-release.d
635 echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
636 echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
637 echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
638 cat <<EOF >/run/confexts/test/etc/testscript
639 #!/bin/bash
640 echo "This should not happen"
641 EOF
642 chmod +x /run/confexts/test/etc/testscript
643 systemd-confext merge
644 grep -q -F "MARKER_CONFEXT_123" /etc/testfile
645 (! /etc/testscript)
646 systemd-confext status
647 systemd-confext unmerge
648 rm -rf /run/confexts/
649
650 unsquashfs -no-xattrs -d /tmp/img "${image}.raw"
651 systemd-run --unit=test-root-ephemeral \
652 -p RootDirectory=/tmp/img \
653 -p RootEphemeral=yes \
654 -p Type=exec \
655 bash -c "touch /abc && sleep infinity"
656 test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
657 systemctl stop test-root-ephemeral
658 # shellcheck disable=SC2016
659 timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
660 test ! -f /tmp/img/abc
661
662 systemd-dissect --mtree /tmp/img
663 systemd-dissect --list /tmp/img
664
665 read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
666 test "$SHA256SUM1" != ""
667
668 echo abc > abc
669 systemd-dissect --copy-to /tmp/img abc /abc
670 test -f /tmp/img/abc
671
672 # Test for dissect tool support with systemd-sysext
673 mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
674 echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
675 echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
676 echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
677 mksquashfs testkit/ testkit.raw
678 cp testkit.raw /run/extensions/
679 unsquashfs -l /run/extensions/testkit.raw
680 systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
681 systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
682 systemd-sysext merge
683 systemd-sysext status
684 grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
685 systemd-sysext unmerge
686 rm -rf /run/extensions/ testkit/
687
688 # Test for dissect tool support with systemd-confext
689 mkdir -p /run/confexts/ testjob/etc/extension-release.d/
690 echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
691 echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
692 echo "MARKER_CONFEXT_123" >testjob/etc/testfile
693 mksquashfs testjob/ testjob.raw
694 cp testjob.raw /run/confexts/
695 unsquashfs -l /run/confexts/testjob.raw
696 systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
697 systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
698 systemd-confext merge
699 systemd-confext status
700 grep -q -F "MARKER_CONFEXT_123" /etc/testfile
701 systemd-confext unmerge
702 rm -rf /run/confexts/ testjob/
703
704 systemd-run -P -p RootImage="${image}.raw" cat /run/host/os-release | cmp "${os_release}"
705
706 # Test that systemd-sysext reloads the daemon.
707 mkdir -p /var/lib/extensions/
708 ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
709 systemd-sysext merge --no-reload
710 # the service should not be running
711 if systemctl --quiet is-active foo.service; then
712 echo "foo.service should not be active"
713 exit 1
714 fi
715 systemd-sysext unmerge --no-reload
716 systemd-sysext merge
717 for RETRY in $(seq 60) LAST; do
718 if journalctl --boot --unit foo.service | grep -q -P 'echo\[[0-9]+\]: foo'; then
719 break
720 fi
721 if [ "${RETRY}" = LAST ]; then
722 echo "Output of foo.service not found"
723 exit 1
724 fi
725 sleep 0.5
726 done
727 systemd-sysext unmerge --no-reload
728 # Grep on the Warning to find the warning helper mentioning the daemon reload.
729 systemctl status foo.service 2>&1 | grep -q -F "Warning"
730 systemd-sysext merge
731 systemd-sysext unmerge
732 systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
733 rm /var/lib/extensions/app-reload.raw
734
735 # Test systemd-repart --make-ddi=:
736 if command -v mksquashfs >/dev/null 2>&1; then
737
738 openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" -x509 -sha256 -nodes -days 365 -newkey rsa:4096 -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
739
740 mkdir -p /tmp/test-50-confext/etc/extension-release.d/
741
742 echo "foobar50" > /tmp/test-50-confext/etc/waldo
743
744 ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
745
746 mkdir -p /run/confexts
747
748 SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -C -s /tmp/test-50-confext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/confexts/waldo.confext.raw
749 rm -rf /tmp/test-50-confext
750
751 mkdir -p /run/verity.d
752 cp /tmp/test-50-cert.crt /run/verity.d/
753 systemd-dissect --mtree /run/confexts/waldo.confext.raw
754
755 systemd-confext refresh
756
757 read -r X < /etc/waldo
758 test "$X" = foobar50
759
760 rm /run/confexts/waldo.confext.raw
761
762 systemd-confext refresh
763
764 (! test -f /etc/waldo )
765
766 mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
767
768 # Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
769 # Minimize= logic is working.
770 truncate --size=50M /tmp/test-50-sysext/usr/waldo
771
772 ( grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release ; echo IMAGE_ID=waldo ; echo IMAGE_VERSION=7 ) > /tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
773
774 mkdir -p /run/extensions
775
776 SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs systemd-repart -S -s /tmp/test-50-sysext --certificate=/tmp/test-50-cert.crt --private-key=/tmp/test-50-privkey.key /run/extensions/waldo.sysext.raw
777
778 systemd-dissect --mtree /run/extensions/waldo.sysext.raw
779
780 systemd-sysext refresh
781
782 test -f /usr/waldo
783
784 rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
785
786 systemd-sysext refresh
787
788 (! test -f /usr/waldo)
789 fi
790
791 # Sneak in a couple of expected-to-fail invocations to cover
792 # https://github.com/systemd/systemd/issues/29610
793 (! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
794 (! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
795 (! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
796 (! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
797
798 # general systemd-sysext tests
799
800 shopt -s extglob
801
802 die() {
803 echo "${*}"
804 exit 1
805 }
806
807 prep_root() {
808 local r=${1}; shift
809 local h=${1}; shift
810
811 if [[ -d ${r} ]]; then
812 die "${r@Q} is being reused as a root, possibly a result of copy-pasting some test case and forgetting to rename the root directory name"
813 fi
814
815 mkdir -p "${r}${h}" "${r}/usr/lib" "${r}/var/lib/extensions" "${r}/var/lib/extensions.mutable"
816 }
817
818 prep_env() {
819 local mode=${1}; shift
820
821 export SYSTEMD_SYSEXT_MUTABLE_MODE=${mode}
822 }
823
824 drop_env() {
825 unset -v SYSTEMD_SYSEXT_MUTABLE_MODE
826 }
827
828 gen_os_release() {
829 local r=${1}; shift
830
831 {
832 echo "ID=testtest"
833 echo "VERSION=1.2.3"
834 } >"${r}/usr/lib/os-release"
835 }
836
837 gen_test_ext_image() {
838 local r=${1}; shift
839 local h=${1}; shift
840
841 local n d f
842
843 n='test-extension'
844 d="${r}/var/lib/extensions/${n}"
845 f="${d}/usr/lib/extension-release.d/extension-release.${n}"
846 mkdir -p "$(dirname "${f}")"
847 echo "ID=_any" >"${f}"
848 mkdir -p "${d}/${h}"
849 touch "${d}${h}/preexisting-file-in-extension-image"
850 }
851
852 hierarchy_ext_mut_path() {
853 local r=${1}; shift
854 local h=${1}; shift
855
856 # /a/b/c -> a.b.c
857 local n=${h}
858 n="${n##+(/)}"
859 n="${n%%+(/)}"
860 n="${n//\//.}"
861
862 printf '%s' "${r}/var/lib/extensions.mutable/${n}"
863 }
864
865 prep_ext_mut() {
866 local p=${1}; shift
867
868 mkdir -p "${p}"
869 touch "${p}/preexisting-file-in-extensions-mutable"
870 }
871
872 make_ro() {
873 local r=${1}; shift
874 local h=${1}; shift
875
876 mount -o bind "${r}${h}" "${r}${h}"
877 mount -o bind,remount,ro "${r}${h}"
878 }
879
880 prep_hierarchy() {
881 local r=${1}; shift
882 local h=${1}; shift
883
884 touch "${r}${h}/preexisting-file-in-hierarchy"
885 }
886
887 prep_ro_hierarchy() {
888 local r=${1}; shift
889 local h=${1}; shift
890
891 prep_hierarchy "${r}" "${h}"
892 make_ro "${r}" "${h}"
893 }
894
895 # extra args:
896 # "e" for checking for the preexisting file in extension
897 # "h" for checking for the preexisting file in hierarchy
898 # "u" for checking for the preexisting file in upperdir
899 check_usual_suspects() {
900 local root=${1}; shift
901 local hierarchy=${1}; shift
902 local message=${1}; shift
903
904 local arg
905 # shellcheck disable=SC2034 # the variables below are used indirectly
906 local e='' h='' u=''
907
908 for arg; do
909 case ${arg} in
910 e|h|u)
911 local -n v=${arg}
912 v=x
913 unset -n v
914 ;;
915 *)
916 die "invalid arg to ${0}: ${arg@Q}"
917 ;;
918 esac
919 done
920
921 # var name, file name
922 local pairs=(
923 e:preexisting-file-in-extension-image
924 h:preexisting-file-in-hierarchy
925 u:preexisting-file-in-extensions-mutable
926 )
927 local pair name file desc full_path
928 for pair in "${pairs[@]}"; do
929 name=${pair%%:*}
930 file=${pair#*:}
931 desc=${file//-/ }
932 full_path="${root}${hierarchy}/${file}"
933 local -n v=${name}
934 if [[ -n ${v} ]]; then
935 test -f "${full_path}" || {
936 ls -la "$(dirname "${full_path}")"
937 die "${desc} is missing ${message}"
938 }
939 else
940 test ! -f "${full_path}" || {
941 ls -la "$(dirname "${full_path}")"
942 die "${desc} unexpectedly exists ${message}"
943 }
944 fi
945 unset -n v
946 done
947 }
948
949 check_usual_suspects_after_merge() {
950 local r=${1}; shift
951 local h=${1}; shift
952
953 check_usual_suspects "${r}" "${h}" "after merge" "${@}"
954 }
955
956 check_usual_suspects_after_unmerge() {
957 local r=${1}; shift
958 local h=${1}; shift
959
960 check_usual_suspects "${r}" "${h}" "after unmerge" "${@}"
961 }
962
963 drop_env
964
965 #
966 # no extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
967 # mutability disabled by default
968 #
969 # read-only merged
970 #
971
972
973 fake_root=${fake_roots_dir}/simple-read-only-with-read-only-hierarchy
974 hierarchy=/usr
975
976 prep_root "${fake_root}" "${hierarchy}"
977 gen_os_release "${fake_root}"
978 gen_test_ext_image "${fake_root}" "${hierarchy}"
979
980 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
981
982 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
983
984 # run systemd-sysext
985 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
986
987 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
988 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
989
990 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
991
992 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
993
994 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only after unmerge"
995
996
997 #
998 # no extension data in /var/lib/extensions.mutable/…, mutable hierarchy,
999 # mutability disabled by default
1000 #
1001 # read-only merged
1002 #
1003
1004
1005 fake_root=${fake_roots_dir}/simple-read-only-with-mutable-hierarchy
1006 hierarchy=/usr
1007
1008 prep_root "${fake_root}" "${hierarchy}"
1009 gen_os_release "${fake_root}"
1010 gen_test_ext_image "${fake_root}" "${hierarchy}"
1011
1012 prep_hierarchy "${fake_root}" "${hierarchy}"
1013
1014 touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs" || die "${fake_root}${hierarchy} is not mutable"
1015
1016 # run systemd-sysext
1017 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1018
1019 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1020 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1021
1022 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1023
1024 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1025
1026 touch "${fake_root}${hierarchy}/should-succeed-on-mutable-fs-again" || die "${fake_root}${hierarchy} is not mutable after unmerge"
1027
1028
1029 #
1030 # no extension data in /var/lib/extensions.mutable/…, no hierarchy either,
1031 # mutability disabled by default
1032 #
1033 # read-only merged
1034 #
1035
1036
1037 fake_root=${fake_roots_dir}/simple-read-only-with-missing-hierarchy
1038 hierarchy=/opt
1039
1040 prep_root "${fake_root}" "${hierarchy}"
1041 rmdir "${fake_root}/${hierarchy}"
1042 gen_os_release "${fake_root}"
1043 gen_test_ext_image "${fake_root}" "${hierarchy}"
1044
1045 # run systemd-sysext
1046 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1047
1048 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1049 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
1050
1051 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1052
1053 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
1054
1055
1056 #
1057 # no extension data in /var/lib/extensions.mutable/…, an empty hierarchy,
1058 # mutability disabled by default
1059 #
1060 # read-only merged
1061 #
1062
1063
1064 fake_root=${fake_roots_dir}/simple-read-only-with-empty-hierarchy
1065 hierarchy=/opt
1066
1067 prep_root "${fake_root}" "${hierarchy}"
1068 gen_os_release "${fake_root}"
1069 gen_test_ext_image "${fake_root}" "${hierarchy}"
1070
1071 make_ro "${fake_root}" "${hierarchy}"
1072
1073 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1074
1075 # run systemd-sysext
1076 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1077
1078 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1079 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e
1080
1081 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1082
1083 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
1084
1085
1086 #
1087 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy, mutability disabled-by-default
1088 #
1089 # read-only merged
1090 #
1091
1092
1093 fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy-disabled
1094 hierarchy=/usr
1095
1096 prep_root "${fake_root}" "${hierarchy}"
1097 gen_os_release "${fake_root}"
1098 gen_test_ext_image "${fake_root}" "${hierarchy}"
1099
1100 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1101 prep_ext_mut "${ext_data_path}"
1102
1103 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1104
1105 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1106
1107 # run systemd-sysext
1108 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1109
1110 touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
1111 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1112
1113 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1114
1115 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1116
1117
1118 #
1119 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy, auto-mutability
1120 #
1121 # mutable merged
1122 #
1123
1124
1125 fake_root=${fake_roots_dir}/simple-mutable-with-read-only-hierarchy
1126 hierarchy=/usr
1127
1128 prep_root "${fake_root}" "${hierarchy}"
1129 gen_os_release "${fake_root}"
1130 gen_test_ext_image "${fake_root}" "${hierarchy}"
1131
1132 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1133 prep_ext_mut "${ext_data_path}"
1134
1135 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1136
1137 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1138
1139 # run systemd-sysext
1140 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1141
1142 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1143 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1144 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1145
1146 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1147
1148 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1149 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1150 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1151
1152
1153 #
1154 # extension data in /var/lib/extensions.mutable/…, missing hierarchy,
1155 # auto-mutability
1156 #
1157 # mutable merged
1158 #
1159
1160
1161 fake_root=${fake_roots_dir}/simple-mutable-with-missing-hierarchy
1162 hierarchy=/opt
1163
1164 prep_root "${fake_root}" "${hierarchy}"
1165 rmdir "${fake_root}/${hierarchy}"
1166 gen_os_release "${fake_root}"
1167 gen_test_ext_image "${fake_root}" "${hierarchy}"
1168
1169 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1170 prep_ext_mut "${ext_data_path}"
1171
1172 # run systemd-sysext
1173 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1174
1175 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1176 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
1177 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1178
1179 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1180
1181 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
1182 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1183 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1184
1185
1186 #
1187 # extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability
1188 #
1189 # mutable merged
1190 #
1191
1192
1193 fake_root=${fake_roots_dir}/simple-mutable-with-empty-hierarchy
1194 hierarchy=/opt
1195
1196 prep_root "${fake_root}" "${hierarchy}"
1197 gen_os_release "${fake_root}"
1198 gen_test_ext_image "${fake_root}" "${hierarchy}"
1199
1200 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1201 prep_ext_mut "${ext_data_path}"
1202
1203 make_ro "${fake_root}" "${hierarchy}"
1204
1205 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1206
1207 # run systemd-sysext
1208 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1209
1210 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1211 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e u
1212 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1213
1214 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1215
1216 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}"
1217 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1218 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1219
1220
1221 #
1222 # /var/lib/extensions.mutable/… is a symlink to /some/other/dir, read-only
1223 # hierarchy, auto-mutability
1224 #
1225 # mutable merged
1226 #
1227
1228
1229 fake_root=${fake_roots_dir}/mutable-symlink-with-read-only-hierarchy
1230 hierarchy=/usr
1231
1232 prep_root "${fake_root}" "${hierarchy}"
1233 gen_os_release "${fake_root}"
1234 gen_test_ext_image "${fake_root}" "${hierarchy}"
1235
1236 # generate extension writable data
1237 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1238 real_ext_dir="${fake_root}/upperdir"
1239 prep_ext_mut "${real_ext_dir}"
1240 ln -sfTr "${real_ext_dir}" "${ext_data_path}"
1241
1242 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1243
1244 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1245
1246 # run systemd-sysext
1247 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1248
1249 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1250 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1251 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1252 test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1253
1254 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1255
1256 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1257 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1258 test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1259 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1260
1261
1262 #
1263 # /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, auto-mutability
1264 #
1265 # for this to work, hierarchy must be mutable
1266 #
1267 # mutable merged
1268 #
1269
1270
1271 fake_root=${fake_roots_dir}/mutable-self-upper
1272 hierarchy=/usr
1273
1274 prep_root "${fake_root}" "${hierarchy}"
1275 gen_os_release "${fake_root}"
1276 gen_test_ext_image "${fake_root}" "${hierarchy}"
1277
1278 # generate extension writable data
1279 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1280 real_ext_dir="${fake_root}${hierarchy}"
1281 prep_ext_mut "${real_ext_dir}"
1282 ln -sfTr "${real_ext_dir}" "${ext_data_path}"
1283
1284 # prepare writable hierarchy
1285 touch "${fake_root}${hierarchy}/preexisting-file-in-hierarchy"
1286
1287 # run systemd-sysext
1288 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1289
1290 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1291 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1292 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1293 test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1294
1295 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1296
1297 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h u
1298 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1299 test -f "${real_ext_dir}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1300
1301
1302 #
1303 # /var/lib/extensions.mutable/… is a symlink to the hierarchy itself, which is
1304 # read-only, auto-mutability
1305 #
1306 # expecting a failure here
1307 #
1308
1309
1310 fake_root=${fake_roots_dir}/failure-self-upper-ro
1311 hierarchy=/usr
1312
1313 prep_root "${fake_root}" "${hierarchy}"
1314 gen_os_release "${fake_root}"
1315 gen_test_ext_image "${fake_root}" "${hierarchy}"
1316
1317 # generate extension writable data
1318 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1319 real_ext_dir="${fake_root}${hierarchy}"
1320 prep_ext_mut "${real_ext_dir}"
1321 ln -sfTr "${real_ext_dir}" "${ext_data_path}"
1322
1323 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1324
1325 # run systemd-sysext
1326 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge && die "expected merge to fail"
1327
1328
1329 #
1330 # /var/lib/extensions.mutable/… is a dangling symlink, auto-mutability
1331 #
1332 # read-only merged
1333 #
1334
1335
1336 fake_root=${fake_roots_dir}/read-only-mutable-dangling-symlink
1337 hierarchy=/usr
1338
1339 prep_root "${fake_root}" "${hierarchy}"
1340 gen_os_release "${fake_root}"
1341 gen_test_ext_image "${fake_root}" "${hierarchy}"
1342
1343 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1344 ln -sfTr "/should/not/exist/" "${ext_data_path}"
1345
1346 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1347
1348 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1349
1350 # run systemd-sysext
1351 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1352
1353 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1354
1355 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1356
1357 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1358
1359 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1360
1361
1362 #
1363 # /var/lib/extensions.mutable/… exists, but it's ignored, mutability disabled explicitly
1364 #
1365 # read-only merged
1366 #
1367
1368
1369 fake_root=${fake_roots_dir}/disabled
1370 hierarchy=/usr
1371
1372 prep_root "${fake_root}" "${hierarchy}"
1373 gen_os_release "${fake_root}"
1374 gen_test_ext_image "${fake_root}" "${hierarchy}"
1375
1376 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1377 prep_ext_mut "${ext_data_path}"
1378
1379 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1380
1381 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1382
1383 # run systemd-sysext
1384 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
1385
1386 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1387
1388 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1389
1390 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1391
1392 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1393
1394
1395 #
1396 # /var/lib/extensions.mutable/… exists, but it's imported instead
1397 #
1398 # read-only merged
1399 #
1400
1401
1402 fake_root=${fake_roots_dir}/imported
1403 hierarchy=/usr
1404
1405 prep_root "${fake_root}" "${hierarchy}"
1406 gen_os_release "${fake_root}"
1407 gen_test_ext_image "${fake_root}" "${hierarchy}"
1408
1409 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1410 prep_ext_mut "${ext_data_path}"
1411
1412 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1413
1414 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1415
1416 # run systemd-sysext
1417 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge
1418
1419 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1420
1421 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1422
1423 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1424
1425 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1426
1427
1428 #
1429 # /var/lib/extensions.mutable/… does not exist, but mutability is enabled
1430 # explicitly
1431 #
1432 # mutable merged
1433 #
1434
1435
1436 fake_root=${fake_roots_dir}/enabled
1437 hierarchy=/usr
1438
1439 prep_root "${fake_root}" "${hierarchy}"
1440 gen_os_release "${fake_root}"
1441 gen_test_ext_image "${fake_root}" "${hierarchy}"
1442
1443 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1444
1445 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1446
1447 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1448
1449 test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
1450
1451 # run systemd-sysext
1452 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=yes merge
1453
1454 test -d "${ext_data_path}" || die "extensions.mutable should exist now"
1455 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1456 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1457 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1458
1459 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1460
1461 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1462 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1463 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1464
1465
1466 #
1467 # /var/lib/extensions.mutable/… does not exist, auto-mutability
1468 #
1469 # read-only merged
1470 #
1471
1472
1473 fake_root=${fake_roots_dir}/simple-read-only-explicit
1474 hierarchy=/usr
1475
1476 prep_root "${fake_root}" "${hierarchy}"
1477 gen_os_release "${fake_root}"
1478 gen_test_ext_image "${fake_root}" "${hierarchy}"
1479
1480 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1481
1482 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1483
1484 # run systemd-sysext
1485 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1486
1487 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1488 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1489
1490 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1491
1492 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1493
1494
1495 #
1496 # /var/lib/extensions.mutable/… does not exist, but mutability is enabled
1497 # through an env var
1498 #
1499 # mutable merged
1500 #
1501
1502
1503 fake_root=${fake_roots_dir}/enabled-env-var
1504 hierarchy=/usr
1505
1506 prep_env "yes"
1507 prep_root "${fake_root}" "${hierarchy}"
1508 gen_os_release "${fake_root}"
1509 gen_test_ext_image "${fake_root}" "${hierarchy}"
1510
1511 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1512
1513 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1514
1515 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1516
1517 test ! -d "${ext_data_path}" || die "extensions.mutable should not exist"
1518
1519 # run systemd-sysext
1520 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1521
1522 test -d "${ext_data_path}" || die "extensions.mutable should exist now"
1523 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1524 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1525 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1526
1527 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1528
1529 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1530 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1531 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1532 drop_env
1533
1534
1535 #
1536 # /var/lib/extensions.mutable/… does not exist, auto-mutability through an env
1537 # var
1538 #
1539 # read-only merged
1540 #
1541
1542
1543 fake_root=${fake_roots_dir}/read-only-auto-env-var
1544 hierarchy=/usr
1545
1546 prep_env "auto"
1547 prep_root "${fake_root}" "${hierarchy}"
1548 gen_os_release "${fake_root}"
1549 gen_test_ext_image "${fake_root}" "${hierarchy}"
1550
1551 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1552
1553 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1554
1555 # run systemd-sysext
1556 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=auto merge
1557
1558 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1559 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1560
1561 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1562
1563 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1564 drop_env
1565
1566
1567 #
1568 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1569 # auto-mutability through an env var
1570 #
1571 # mutable merged
1572 #
1573
1574
1575 fake_root=${fake_roots_dir}/auto-mutable-env-var
1576 hierarchy=/usr
1577
1578 prep_env "auto"
1579 prep_root "${fake_root}" "${hierarchy}"
1580 gen_os_release "${fake_root}"
1581 gen_test_ext_image "${fake_root}" "${hierarchy}"
1582
1583 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1584 prep_ext_mut "${ext_data_path}"
1585
1586 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1587
1588 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1589
1590 # run systemd-sysext
1591 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1592
1593 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1594 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1595 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable is not stored in expected location"
1596
1597 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1598
1599 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1600 test -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable disappeared from writable storage after unmerge"
1601 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1602 drop_env
1603
1604
1605 #
1606 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1607 # mutability disabled through an env var
1608 #
1609 # read-only merged
1610 #
1611
1612
1613 fake_root=${fake_roots_dir}/env-var-disabled
1614 hierarchy=/usr
1615
1616 prep_env "no"
1617 prep_root "${fake_root}" "${hierarchy}"
1618 gen_os_release "${fake_root}"
1619 gen_test_ext_image "${fake_root}" "${hierarchy}"
1620
1621 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1622 prep_ext_mut "${ext_data_path}"
1623
1624 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1625
1626 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1627
1628 # run systemd-sysext
1629 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1630
1631 touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
1632 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1633
1634 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1635
1636 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1637 drop_env
1638
1639
1640 #
1641 # /var/lib/extensions.mutable/… exists, but it's imported instead through an
1642 # env var
1643 #
1644 # read-only merged
1645 #
1646
1647
1648 fake_root=${fake_roots_dir}/imported-env-var
1649 hierarchy=/usr
1650
1651 prep_env "import"
1652 prep_root "${fake_root}" "${hierarchy}"
1653 gen_os_release "${fake_root}"
1654 gen_test_ext_image "${fake_root}" "${hierarchy}"
1655
1656 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1657 prep_ext_mut "${ext_data_path}"
1658
1659 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1660
1661 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1662
1663 # run systemd-sysext
1664 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1665
1666 touch "${fake_root}${hierarchy}/should-still-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1667
1668 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1669
1670 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1671
1672 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1673 drop_env
1674
1675
1676 #
1677 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1678 # mutability enabled through an env var, but overridden with a command-line
1679 # option
1680 #
1681 # read-only merged
1682 #
1683
1684
1685 fake_root=${fake_roots_dir}/env-var-overridden
1686 hierarchy=/usr
1687
1688 prep_env "yes"
1689 prep_root "${fake_root}" "${hierarchy}"
1690 gen_os_release "${fake_root}"
1691 gen_test_ext_image "${fake_root}" "${hierarchy}"
1692
1693 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1694 prep_ext_mut "${ext_data_path}"
1695
1696 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1697
1698 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1699
1700 # run systemd-sysext
1701 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=no merge
1702
1703 touch "${fake_root}${hierarchy}/should-be-read-only" && die "${fake_root}${hierarchy} is not read-only"
1704 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1705
1706 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1707
1708 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1709 drop_env
1710
1711
1712 #
1713 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1714 # ephemeral mutability, so extension data contents are ignored
1715 #
1716 # mutable merged
1717 #
1718
1719
1720 fake_root=${fake_roots_dir}/ephemeral
1721 hierarchy=/usr
1722
1723 prep_root "${fake_root}" "${hierarchy}"
1724 gen_os_release "${fake_root}"
1725 gen_test_ext_image "${fake_root}" "${hierarchy}"
1726
1727 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1728 prep_ext_mut "${ext_data_path}"
1729
1730 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1731
1732 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1733
1734 # run systemd-sysext
1735 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral merge
1736
1737 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1738 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1739 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
1740
1741 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1742
1743 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1744 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
1745 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1746
1747
1748 #
1749 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1750 # ephemeral mutability through an env var, so extension data contents are
1751 # ignored
1752 #
1753 # mutable merged
1754 #
1755
1756
1757 fake_root=${fake_roots_dir}/ephemeral-env-var
1758 hierarchy=/usr
1759
1760 prep_env "ephemeral"
1761 prep_root "${fake_root}" "${hierarchy}"
1762 gen_os_release "${fake_root}"
1763 gen_test_ext_image "${fake_root}" "${hierarchy}"
1764
1765 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1766 prep_ext_mut "${ext_data_path}"
1767
1768 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1769
1770 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1771
1772 # run systemd-sysext
1773 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1774
1775 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1776 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h
1777 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
1778
1779 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1780
1781 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1782 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
1783 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1784 drop_env
1785
1786
1787 #
1788 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1789 # ephemeral import mutability, so extension data contents are imported too
1790 #
1791 # mutable merged
1792 #
1793
1794
1795 fake_root=${fake_roots_dir}/ephemeral-import
1796 hierarchy=/usr
1797
1798 prep_root "${fake_root}" "${hierarchy}"
1799 gen_os_release "${fake_root}"
1800 gen_test_ext_image "${fake_root}" "${hierarchy}"
1801
1802 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1803 prep_ext_mut "${ext_data_path}"
1804
1805 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1806
1807 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1808
1809 # run systemd-sysext
1810 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge
1811
1812 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1813 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1814 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
1815
1816 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1817
1818 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1819 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
1820 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1821
1822
1823 #
1824 # extension data in /var/lib/extensions.mutable/…, read-only hierarchy,
1825 # ephemeral mutability through an env var, so extension data contents are
1826 # imported too
1827 #
1828 # mutable merged
1829 #
1830
1831
1832 fake_root=${fake_roots_dir}/ephemeral-import-env-var
1833 hierarchy=/usr
1834
1835 prep_env "ephemeral-import"
1836 prep_root "${fake_root}" "${hierarchy}"
1837 gen_os_release "${fake_root}"
1838 gen_test_ext_image "${fake_root}" "${hierarchy}"
1839
1840 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1841 prep_ext_mut "${ext_data_path}"
1842
1843 prep_ro_hierarchy "${fake_root}" "${hierarchy}"
1844
1845 touch "${fake_root}${hierarchy}/should-fail-on-read-only-fs" && die "${fake_root}${hierarchy} is not read-only"
1846
1847 # run systemd-sysext
1848 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" merge
1849
1850 touch "${fake_root}${hierarchy}/now-is-mutable" || die "${fake_root}${hierarchy} is not mutable"
1851 check_usual_suspects_after_merge "${fake_root}" "${hierarchy}" e h u
1852 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not be stored in extension data"
1853
1854 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" unmerge
1855
1856 check_usual_suspects_after_unmerge "${fake_root}" "${hierarchy}" h
1857 test ! -f "${ext_data_path}/now-is-mutable" || die "now-is-mutable should not appear in writable storage after unmerge"
1858 test ! -f "${fake_root}${hierarchy}/now-is-mutable" || die "now-is-mutable did not disappear from hierarchy after unmerge"
1859 drop_env
1860
1861
1862 #
1863 # extension data pointing to mutable hierarchy, ephemeral import mutability
1864 #
1865 # expecting a failure here
1866 #
1867
1868
1869 fake_root=${fake_roots_dir}/ephemeral-import-self
1870 hierarchy=/usr
1871
1872 prep_root "${fake_root}" "${hierarchy}"
1873 gen_os_release "${fake_root}"
1874 gen_test_ext_image "${fake_root}" "${hierarchy}"
1875
1876 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1877 real_ext_dir="${fake_root}${hierarchy}"
1878 prep_ext_mut "${real_ext_dir}"
1879 ln -sfTr "${real_ext_dir}" "${ext_data_path}"
1880
1881 prep_hierarchy "${fake_root}" "${hierarchy}"
1882
1883 touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
1884
1885 # run systemd-sysext
1886 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=ephemeral-import merge && die 'expected merge to fail'
1887
1888
1889 #
1890 # extension data pointing to mutable hierarchy, import mutability
1891 #
1892 # expecting a failure here
1893 #
1894
1895
1896 fake_root=${fake_roots_dir}/import-self
1897 hierarchy=/usr
1898
1899 prep_root "${fake_root}" "${hierarchy}"
1900 gen_os_release "${fake_root}"
1901 gen_test_ext_image "${fake_root}" "${hierarchy}"
1902
1903 ext_data_path=$(hierarchy_ext_mut_path "${fake_root}" "${hierarchy}")
1904 real_ext_dir="${fake_root}${hierarchy}"
1905 prep_ext_mut "${real_ext_dir}"
1906 ln -sfTr "${real_ext_dir}" "${ext_data_path}"
1907
1908 prep_hierarchy "${fake_root}" "${hierarchy}"
1909
1910 touch "${fake_root}${hierarchy}/should-succeed-on-read-only-fs" || die "${fake_root}${hierarchy} is not mutable"
1911
1912 # run systemd-sysext
1913 SYSTEMD_SYSEXT_HIERARCHIES="${hierarchy}" systemd-sysext --root="${fake_root}" --mutable=import merge && die 'expected merge to fail'
1914
1915
1916 #
1917 # done
1918 #
1919
1920
1921 touch /testok