summaryrefslogtreecommitdiffstats
path: root/test/units/TEST-60-MOUNT-RATELIMIT.sh
blob: a0e99dc40f0d899a6a281697b2b670dd05f8f0b5 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail

# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh

teardown_test_dependencies() (
    set +eux

    if mountpoint /tmp/deptest; then
        umount /tmp/deptest
    fi

    if [[ -n "${LOOP}" ]]; then
        losetup -d "${LOOP}" || :
    fi
    if [[ -n "${LOOP_0}" ]]; then
        losetup -d "${LOOP_0}" || :
    fi
    if [[ -n "${LOOP_1}" ]]; then
        losetup -d "${LOOP_1}" || :
    fi

    rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
    rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img

    rm -f /run/systemd/system/tmp-deptest.mount
    systemctl daemon-reload

    return 0
)

setup_loop() {
    truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
    sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
label:gpt

name="loop${1?}-part1"
EOF
    LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
    udevadm wait --settle --timeout=10 "${LOOP}"
    udevadm lock --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
}

check_dependencies() {
    local escaped_0 escaped_1 after

    escaped_0=$(systemd-escape -p "${LOOP_0}p1")
    escaped_1=$(systemd-escape -p "${LOOP_1}p1")

    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
        after=$(systemctl show --property=After --value tmp-deptest.mount)
        assert_not_in "local-fs-pre.target" "$after"
        assert_in "remote-fs-pre.target" "$after"
        assert_in "network.target" "$after"
    fi

    # mount LOOP_0
    mount -t ext4 "${LOOP_0}p1" /tmp/deptest
    sleep 1
    after=$(systemctl show --property=After --value tmp-deptest.mount)
    assert_in "local-fs-pre.target" "$after"
    assert_not_in "remote-fs-pre.target" "$after"
    assert_not_in "network.target" "$after"
    assert_in "${escaped_0}.device" "$after"
    assert_in "blockdev@${escaped_0}.target" "$after"
    assert_not_in "${escaped_1}.device" "$after"
    assert_not_in "blockdev@${escaped_1}.target" "$after"
    umount /tmp/deptest

    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
        after=$(systemctl show --property=After --value tmp-deptest.mount)
        assert_not_in "local-fs-pre.target" "$after"
        assert_in "remote-fs-pre.target" "$after"
        assert_in "network.target" "$after"
    fi

    # mount LOOP_1 (using fake _netdev option)
    mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
    sleep 1
    after=$(systemctl show --property=After --value tmp-deptest.mount)
    assert_not_in "local-fs-pre.target" "$after"
    assert_in "remote-fs-pre.target" "$after"
    assert_in "network.target" "$after"
    assert_not_in "${escaped_0}.device" "$after"
    assert_not_in "blockdev@${escaped_0}.target" "$after"
    assert_in "${escaped_1}.device" "$after"
    assert_in "blockdev@${escaped_1}.target" "$after"
    umount /tmp/deptest

    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
        after=$(systemctl show --property=After --value tmp-deptest.mount)
        assert_not_in "local-fs-pre.target" "$after"
        assert_in "remote-fs-pre.target" "$after"
        assert_in "network.target" "$after"
    fi

    # mount tmpfs
    mount -t tmpfs tmpfs /tmp/deptest
    sleep 1
    after=$(systemctl show --property=After --value tmp-deptest.mount)
    assert_in "local-fs-pre.target" "$after"
    assert_not_in "remote-fs-pre.target" "$after"
    assert_not_in "network.target" "$after"
    assert_not_in "${escaped_0}.device" "$after"
    assert_not_in "blockdev@${escaped_0}.target" "$after"
    assert_not_in "${escaped_1}.device" "$after"
    assert_not_in "blockdev@${escaped_1}.target" "$after"
    umount /tmp/deptest

    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
        after=$(systemctl show --property=After --value tmp-deptest.mount)
        assert_not_in "local-fs-pre.target" "$after"
        assert_in "remote-fs-pre.target" "$after"
        assert_in "network.target" "$after"
    fi
}

test_dependencies() {
    if systemd-detect-virt --quiet --container; then
        echo "Skipping test_dependencies in container"
        return
    fi

    trap teardown_test_dependencies RETURN

    setup_loop 0
    LOOP_0="${LOOP}"
    LOOP=
    setup_loop 1
    LOOP_1="${LOOP}"
    LOOP=

    mkdir -p /tmp/deptest

    # without .mount file
    check_dependencies

    # create .mount file
    mkdir -p /run/systemd/system
    cat >/run/systemd/system/tmp-deptest.mount <<EOF
[Mount]
Where=/tmp/deptest
What=192.168.0.1:/tmp/mnt
Type=nfs
EOF
    systemctl daemon-reload

    # with .mount file
    check_dependencies
}

test_issue_20329() {
    local tmpdir unit
    tmpdir="$(mktemp -d)"
    unit=$(systemd-escape --suffix mount --path "$tmpdir")

    # Set up test mount unit
    cat >/run/systemd/system/"$unit" <<EOF
[Mount]
What=tmpfs
Where=$tmpdir
Type=tmpfs
Options=defaults,nofail
EOF

    # Start the unit
    systemctl daemon-reload
    systemctl start "$unit"

    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
        echo >&2 "Test mount \"$unit\" unit isn't mounted"
        return 1
    }
    mountpoint -q "$tmpdir"

    trap 'systemctl stop $unit' RETURN

    # Trigger the mount ratelimiting
    cd "$(mktemp -d)"
    mkdir foo
    for _ in {1..50}; do
        mount --bind foo foo
        umount foo
    done

    # Unmount the test mount and start it immediately again via systemd
    umount "$tmpdir"
    systemctl start "$unit"

    # Make sure it is seen as mounted by systemd and it actually is mounted
    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
        echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state"
        return 1
    }

    mountpoint -q "$tmpdir" || {
        echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted"
        return 1
    }
}

test_issue_23796() {
    local mount_path mount_mytmpfs since

    mount_path="$(command -v mount 2>/dev/null)"
    mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
    cat >"$mount_mytmpfs" <<EOF
#!/bin/bash
sleep ".\$RANDOM"
exec -- $mount_path -t tmpfs tmpfs "\$2"
EOF
    chmod +x "$mount_mytmpfs"

    mkdir -p /run/systemd/system
    cat >/run/systemd/system/tmp-hoge.mount <<EOF
[Mount]
What=mytmpfs
Where=/tmp/hoge
Type=mytmpfs
EOF

    # shellcheck disable=SC2064
    trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN

    journalctl --sync
    since="$(date '+%H:%M:%S')"

    for _ in {1..10}; do
        systemctl --no-block start tmp-hoge.mount
        sleep ".$RANDOM"
        systemctl daemon-reexec

        sleep 1

        if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
           journalctl --since="$since" -u tmp-hoge.mount -q --grep "but there is no mount"; then
                exit 1
        fi

        systemctl stop tmp-hoge.mount
    done
}

systemd-analyze log-level debug
systemd-analyze log-target journal

NUM_DIRS=20

# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"

journalctl --sync
TS="$(date '+%H:%M:%S')"

mkdir -p "$LONGPATH"
mount -t tmpfs tmpfs "$LONGPATH"
systemctl daemon-reload

# check that unit is active(mounted)
systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted

# check that relevant part of journal doesn't contain any errors related to unit
[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]

# check that we can successfully stop the mount unit
systemctl stop "$LONGPATH"
rm -rf "$LONGPATH"

# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting

for ((i = 0; i < NUM_DIRS; i++)); do
    mkdir "/tmp/meow${i}"
done

# The following loop may produce many journal entries.
# Let's process all pending entries before testing.
journalctl --sync
TS="$(date '+%H:%M:%S')"

for ((i = 0; i < NUM_DIRS; i++)); do
    mount -t tmpfs tmpfs "/tmp/meow${i}"
done

systemctl daemon-reload
systemctl list-units -t mount tmp-meow* | grep -q tmp-meow

for ((i = 0; i < NUM_DIRS; i++)); do
    umount "/tmp/meow${i}"
done

# Figure out if we have entered the rate limit state.
# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
set +o pipefail
journalctl --sync
if timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) entered rate limit'; then
    journalctl --sync
    timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) left rate limit'
fi
set -o pipefail

# Verify that the mount units are always cleaned up at the end.
# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'

# test for issue #19983 and #23552.
test_dependencies

# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
test_issue_20329

# test for reexecuting with background mount job
test_issue_23796

systemd-analyze log-level info

touch /testok