]> git.proxmox.com Git - mirror_qemu.git/blame - tests/qemu-iotests/308
Merge tag 'pull-target-arm-20240126' of https://git.linaro.org/people/pmaydell/qemu...
[mirror_qemu.git] / tests / qemu-iotests / 308
CommitLineData
e6c79647 1#!/usr/bin/env bash
9dd003a9 2# group: rw
e6c79647
HR
3#
4# Test FUSE exports (in ways that are not captured by the generic
5# tests)
6#
7# Copyright (C) 2020 Red Hat, Inc.
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22
23seq=$(basename "$0")
24echo "QA output created by $seq"
25
26status=1 # failure is the default!
27
28_cleanup()
29{
30 _cleanup_qemu
31 _cleanup_test_img
32 rmdir "$EXT_MP" 2>/dev/null
33 rm -f "$EXT_MP"
34 rm -f "$COPIED_IMG"
35}
36trap "_cleanup; exit \$status" 0 1 2 3 15
37
38# get standard environment, filters and checks
39. ./common.rc
40. ./common.filter
41. ./common.qemu
42
43# Generic format, but needs a plain filename
44_supported_fmt generic
45if [ "$IMGOPTSSYNTAX" = "true" ]; then
46 _unsupported_fmt $IMGFMT
47fi
48# We need the image to have exactly the specified size, and VPC does
49# not allow that by default
50_unsupported_fmt vpc
51
52_supported_proto file # We create the FUSE export manually
53_supported_os Linux # We need /dev/urandom
54
55# $1: Export ID
56# $2: Options (beyond the node-name and ID)
57# $3: Expected return value (defaults to 'return')
58# $4: Node to export (defaults to 'node-format')
59fuse_export_add()
60{
8fc54f94
HR
61 # The grep -v is a filter for errors when /etc/fuse.conf does not contain
62 # user_allow_other. (The error is benign, but it is printed by fusermount
63 # on the first mount attempt, so our export code cannot hide it.)
e6c79647
HR
64 _send_qemu_cmd $QEMU_HANDLE \
65 "{'execute': 'block-export-add',
66 'arguments': {
67 'type': 'fuse',
68 'id': '$1',
69 'node-name': '${4:-node-format}',
70 $2
71 } }" \
72 "${3:-return}" \
8fc54f94
HR
73 | _filter_imgfmt \
74 | grep -v 'option allow_other only allowed if'
e6c79647
HR
75}
76
77# $1: Export ID
78fuse_export_del()
79{
effd60c8 80 capture_events="BLOCK_EXPORT_DELETED" \
e6c79647
HR
81 _send_qemu_cmd $QEMU_HANDLE \
82 "{'execute': 'block-export-del',
83 'arguments': {
84 'id': '$1'
85 } }" \
86 'return'
87
effd60c8 88 _wait_event $QEMU_HANDLE \
e6c79647
HR
89 'BLOCK_EXPORT_DELETED'
90}
91
92# Return the length of the protocol file
93# $1: Protocol node export mount point
94# $2: Original file (to compare)
95get_proto_len()
96{
97 len1=$(stat -c '%s' "$1")
98 len2=$(stat -c '%s' "$2")
99
100 if [ "$len1" != "$len2" ]; then
101 echo 'ERROR: Length of export and original differ:' >&2
102 echo "$len1 != $len2" >&2
103 else
104 echo '(OK: Lengths of export and original are the same)' >&2
105 fi
106
107 echo "$len1"
108}
109
110COPIED_IMG="$TEST_IMG.copy"
111EXT_MP="$TEST_IMG.fuse"
112
113echo '=== Set up ==='
114
115# Create image with random data
116_make_test_img 64M
117$QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io
118
119_launch_qemu
120_send_qemu_cmd $QEMU_HANDLE \
121 "{'execute': 'qmp_capabilities'}" \
122 'return'
123
124# Separate blockdev-add calls for format and protocol so we can remove
125# the format layer later on
126_send_qemu_cmd $QEMU_HANDLE \
127 "{'execute': 'blockdev-add',
128 'arguments': {
129 'driver': 'file',
130 'node-name': 'node-protocol',
131 'filename': '$TEST_IMG'
132 } }" \
133 'return'
134
135_send_qemu_cmd $QEMU_HANDLE \
136 "{'execute': 'blockdev-add',
137 'arguments': {
138 'driver': '$IMGFMT',
139 'node-name': 'node-format',
140 'file': 'node-protocol'
141 } }" \
142 'return'
143
144echo
145echo '=== Mountpoint not present ==='
146
147rmdir "$EXT_MP" 2>/dev/null
148rm -f "$EXT_MP"
149output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error)
150
ea29331b 151if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then
e6c79647
HR
152 _notrun 'No FUSE support'
153fi
154
155echo "$output"
156
157echo
158echo '=== Mountpoint is a directory ==='
159
160mkdir "$EXT_MP"
161fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
162rmdir "$EXT_MP"
163
164echo
165echo '=== Mountpoint is a regular file ==='
166
167touch "$EXT_MP"
168fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'"
169
170# Check that the export presents the same data as the original image
171$QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG"
172
f29add26
HR
173# Some quick chmod tests
174stat -c 'Permissions pre-chmod: %a' "$EXT_MP"
175
176# Verify that we cannot set +w
177chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
178stat -c 'Permissions post-+w: %a' "$EXT_MP"
179
180# But that we can set, say, +x (if we are so inclined)
181chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
182stat -c 'Permissions post-+x: %a' "$EXT_MP"
183
e6c79647
HR
184echo
185echo '=== Mount over existing file ==='
186
187# This is the coolest feature of FUSE exports: You can transparently
188# make images in any format appear as raw images
189fuse_export_add 'export-img' "'mountpoint': '$TEST_IMG'"
190
191# Accesses both exports at the same time, so we get a concurrency test
192$QEMU_IMG compare -f raw -F raw -U "$EXT_MP" "$TEST_IMG"
193
194# Just to be sure, we later want to compare the data offline. Also,
195# this allows us to see that cp works without complaining.
196# (This is not a given, because cp will expect a short read at EOF.
197# Internally, qemu does not allow short reads, so we have to check
198# whether the FUSE export driver lets them work.)
199cp "$TEST_IMG" "$COPIED_IMG"
200
201# $TEST_IMG will be in mode 0400 because it is read-only; we are going
202# to write to the copy, so make it writable
203chmod 0600 "$COPIED_IMG"
204
205echo
206echo '=== Double export ==='
207
208# We have already seen that exporting a node twice works fine, but you
209# cannot export anything twice on the same mount point. The reason is
210# that qemu has to stat the given mount point, and this would have to
211# be answered by the same qemu instance if it already has an export
212# there. However, it cannot answer the stat because it is itself
213# caught up in that same stat.
214fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
215
216echo
217echo '=== Remove export ==='
218
219# Double-check that $EXT_MP appears as a non-empty file (the raw image)
74163add 220$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1
e6c79647
HR
221
222fuse_export_del 'export-mp'
223
224# See that the file appears empty again
74163add 225$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1
e6c79647
HR
226
227echo
228echo '=== Writable export ==='
229
230fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
231
232# Check that writing to the read-only export fails
e2eec281
HR
233output=$($QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
234 | _filter_qemu_io | _filter_testdir | _filter_imgfmt)
235
236# Expected reference output: Opening the file fails because it has no
237# write permission
238reference="Could not open 'TEST_DIR/t.IMGFMT': Permission denied"
239
240if echo "$output" | grep -q "$reference"; then
241 echo "Writing to read-only export failed: OK"
242elif echo "$output" | grep -q "write failed: Permission denied"; then
243 # With CAP_DAC_OVERRIDE (e.g. when running this test as root), the export
244 # can be opened regardless of its file permissions, but writing will then
245 # fail. This is not the result for which we want to test, so count this as
246 # a SKIP.
247 _casenotrun "Opening RO export as R/W succeeded, perhaps because of" \
248 "CAP_DAC_OVERRIDE"
249
250 # Still, write this to the reference output to make the test pass
251 echo "Writing to read-only export failed: OK"
252else
253 echo "Writing to read-only export failed: ERROR"
254 echo "$output"
255fi
e6c79647
HR
256
257# But here it should work
258$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
259
260# (Adjust the copy, too)
261$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$COPIED_IMG" | _filter_qemu_io
262
263echo
264echo '=== Resizing exports ==='
265
266# Here, we need to export the protocol node -- the format layer may
267# not be growable, simply because the format does not support it.
268
269# Remove all exports and the format node first so permissions will not
270# get in the way
271fuse_export_del 'export-mp'
272fuse_export_del 'export-img'
273
274_send_qemu_cmd $QEMU_HANDLE \
275 "{'execute': 'blockdev-del',
276 'arguments': {
277 'node-name': 'node-format'
278 } }" \
279 'return'
280
281# Now export the protocol node
282fuse_export_add \
283 'export-mp' \
284 "'mountpoint': '$EXT_MP', 'writable': true" \
285 'return' \
286 'node-protocol'
287
288echo
289echo '--- Try growing non-growable export ---'
290
291# Get the current size so we can write beyond the EOF
292orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
293orig_disk_usage=$(stat -c '%b' "$TEST_IMG")
294
295# Should fail (exports are non-growable by default)
296# (Note that qemu-io can never write beyond the EOF, so we have to use
297# dd here)
298dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$orig_len 2>&1 \
299 | _filter_testdir | _filter_imgfmt
300
301echo
302echo '--- Resize export ---'
303
304# But we can truncate it explicitly; even with fallocate
305fallocate -o "$orig_len" -l 64k "$EXT_MP"
306
307new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
308if [ "$new_len" != "$((orig_len + 65536))" ]; then
309 echo 'ERROR: Unexpected post-truncate image size:'
310 echo "$new_len != $((orig_len + 65536))"
311else
312 echo 'OK: Post-truncate image size is as expected'
313fi
314
315new_disk_usage=$(stat -c '%b' "$TEST_IMG")
316if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then
317 echo 'OK: Disk usage grew with fallocate'
318else
319 echo 'ERROR: Disk usage did not grow despite fallocate:'
320 echo "$orig_disk_usage => $new_disk_usage"
321fi
322
323echo
324echo '--- Try growing growable export ---'
325
326# Now export as growable
327fuse_export_del 'export-mp'
328fuse_export_add \
329 'export-mp' \
330 "'mountpoint': '$EXT_MP', 'writable': true, 'growable': true" \
331 'return' \
332 'node-protocol'
333
334# Now we should be able to write beyond the EOF
335dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$new_len 2>&1 \
336 | _filter_testdir | _filter_imgfmt
337
338new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
339if [ "$new_len" != "$((orig_len + 131072))" ]; then
340 echo 'ERROR: Unexpected post-grow image size:'
341 echo "$new_len != $((orig_len + 131072))"
342else
343 echo 'OK: Post-grow image size is as expected'
344fi
345
346echo
347echo '--- Shrink export ---'
348
349# Now go back to the original size
350truncate -s "$orig_len" "$EXT_MP"
351
352new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
353if [ "$new_len" != "$orig_len" ]; then
354 echo 'ERROR: Unexpected post-truncate image size:'
355 echo "$new_len != $orig_len"
356else
357 echo 'OK: Post-truncate image size is as expected'
358fi
359
360echo
361echo '=== Tear down ==='
362
363_send_qemu_cmd $QEMU_HANDLE \
364 "{'execute': 'quit'}" \
365 'return'
366
367wait=yes _cleanup_qemu
368
369echo
370echo '=== Compare copy with original ==='
371
372$QEMU_IMG compare -f raw -F $IMGFMT "$COPIED_IMG" "$TEST_IMG"
27e0d8b5
HC
373_cleanup_test_img
374
375echo
376echo '=== Writing zeroes while unmapping ==='
377# Regression test for https://gitlab.com/qemu-project/qemu/-/issues/1507
378_make_test_img 64M
379$QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io
380
381_launch_qemu
382_send_qemu_cmd $QEMU_HANDLE \
383 "{'execute': 'qmp_capabilities'}" \
384 'return'
385
386_send_qemu_cmd $QEMU_HANDLE \
387 "{'execute': 'blockdev-add',
388 'arguments': {
389 'driver': '$IMGFMT',
390 'node-name': 'node-format',
391 'file': {
392 'driver': 'file',
393 'filename': '$TEST_IMG'
394 }
395 } }" \
396 'return'
397
398fuse_export_add 'export' "'mountpoint': '$EXT_MP', 'writable': true"
399
400# Try writing zeroes by unmapping
401$QEMU_IO -f raw -c 'write -zu 0 64M' "$EXT_MP" | _filter_qemu_io
402
403# Check the result
404$QEMU_IO -f raw -c 'read -P 0 0 64M' "$EXT_MP" | _filter_qemu_io
405
406_send_qemu_cmd $QEMU_HANDLE \
407 "{'execute': 'quit'}" \
408 'return'
409
410wait=yes _cleanup_qemu
411
412# Check the original image
413$QEMU_IO -c 'read -P 0 0 64M' "$TEST_IMG" | _filter_qemu_io
414
415_cleanup_test_img
e6c79647
HR
416
417# success, all done
418echo "*** done"
419rm -f $seq.full
420status=0