Merge remote-tracking branch 'remotes/nvme/tags/nvme-fixes-20210407-pull-request...
[qemu/ar7.git] / tests / qemu-iotests / 308
blobf122065d0fec06fdd97168147d9bd8a2315a051d
1 #!/usr/bin/env bash
2 # group: rw
4 # Test FUSE exports (in ways that are not captured by the generic
5 # tests)
7 # Copyright (C) 2020 Red Hat, Inc.
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.
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.
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/>.
23 seq=$(basename "$0")
24 echo "QA output created by $seq"
26 status=1 # failure is the default!
28 _cleanup()
30 _cleanup_qemu
31 _cleanup_test_img
32 rmdir "$EXT_MP" 2>/dev/null
33 rm -f "$EXT_MP"
34 rm -f "$COPIED_IMG"
36 trap "_cleanup; exit \$status" 0 1 2 3 15
38 # get standard environment, filters and checks
39 . ./common.rc
40 . ./common.filter
41 . ./common.qemu
43 # Generic format, but needs a plain filename
44 _supported_fmt generic
45 if [ "$IMGOPTSSYNTAX" = "true" ]; then
46 _unsupported_fmt $IMGFMT
48 # We need the image to have exactly the specified size, and VPC does
49 # not allow that by default
50 _unsupported_fmt vpc
52 _supported_proto file # We create the FUSE export manually
53 _supported_os Linux # We need /dev/urandom
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')
59 fuse_export_add()
61 _send_qemu_cmd $QEMU_HANDLE \
62 "{'execute': 'block-export-add',
63 'arguments': {
64 'type': 'fuse',
65 'id': '$1',
66 'node-name': '${4:-node-format}',
68 } }" \
69 "${3:-return}" \
70 | _filter_imgfmt
73 # $1: Export ID
74 fuse_export_del()
76 _send_qemu_cmd $QEMU_HANDLE \
77 "{'execute': 'block-export-del',
78 'arguments': {
79 'id': '$1'
80 } }" \
81 'return'
83 _send_qemu_cmd $QEMU_HANDLE \
84 '' \
85 'BLOCK_EXPORT_DELETED'
88 # Return the length of the protocol file
89 # $1: Protocol node export mount point
90 # $2: Original file (to compare)
91 get_proto_len()
93 len1=$(stat -c '%s' "$1")
94 len2=$(stat -c '%s' "$2")
96 if [ "$len1" != "$len2" ]; then
97 echo 'ERROR: Length of export and original differ:' >&2
98 echo "$len1 != $len2" >&2
99 else
100 echo '(OK: Lengths of export and original are the same)' >&2
103 echo "$len1"
106 COPIED_IMG="$TEST_IMG.copy"
107 EXT_MP="$TEST_IMG.fuse"
109 echo '=== Set up ==='
111 # Create image with random data
112 _make_test_img 64M
113 $QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io
115 _launch_qemu
116 _send_qemu_cmd $QEMU_HANDLE \
117 "{'execute': 'qmp_capabilities'}" \
118 'return'
120 # Separate blockdev-add calls for format and protocol so we can remove
121 # the format layer later on
122 _send_qemu_cmd $QEMU_HANDLE \
123 "{'execute': 'blockdev-add',
124 'arguments': {
125 'driver': 'file',
126 'node-name': 'node-protocol',
127 'filename': '$TEST_IMG'
128 } }" \
129 'return'
131 _send_qemu_cmd $QEMU_HANDLE \
132 "{'execute': 'blockdev-add',
133 'arguments': {
134 'driver': '$IMGFMT',
135 'node-name': 'node-format',
136 'file': 'node-protocol'
137 } }" \
138 'return'
140 echo
141 echo '=== Mountpoint not present ==='
143 rmdir "$EXT_MP" 2>/dev/null
144 rm -f "$EXT_MP"
145 output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error)
147 if echo "$output" | grep -q "Invalid parameter 'fuse'"; then
148 _notrun 'No FUSE support'
151 echo "$output"
153 echo
154 echo '=== Mountpoint is a directory ==='
156 mkdir "$EXT_MP"
157 fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
158 rmdir "$EXT_MP"
160 echo
161 echo '=== Mountpoint is a regular file ==='
163 touch "$EXT_MP"
164 fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'"
166 # Check that the export presents the same data as the original image
167 $QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG"
169 echo
170 echo '=== Mount over existing file ==='
172 # This is the coolest feature of FUSE exports: You can transparently
173 # make images in any format appear as raw images
174 fuse_export_add 'export-img' "'mountpoint': '$TEST_IMG'"
176 # Accesses both exports at the same time, so we get a concurrency test
177 $QEMU_IMG compare -f raw -F raw -U "$EXT_MP" "$TEST_IMG"
179 # Just to be sure, we later want to compare the data offline. Also,
180 # this allows us to see that cp works without complaining.
181 # (This is not a given, because cp will expect a short read at EOF.
182 # Internally, qemu does not allow short reads, so we have to check
183 # whether the FUSE export driver lets them work.)
184 cp "$TEST_IMG" "$COPIED_IMG"
186 # $TEST_IMG will be in mode 0400 because it is read-only; we are going
187 # to write to the copy, so make it writable
188 chmod 0600 "$COPIED_IMG"
190 echo
191 echo '=== Double export ==='
193 # We have already seen that exporting a node twice works fine, but you
194 # cannot export anything twice on the same mount point. The reason is
195 # that qemu has to stat the given mount point, and this would have to
196 # be answered by the same qemu instance if it already has an export
197 # there. However, it cannot answer the stat because it is itself
198 # caught up in that same stat.
199 fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
201 echo
202 echo '=== Remove export ==='
204 # Double-check that $EXT_MP appears as a non-empty file (the raw image)
205 $QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
207 fuse_export_del 'export-mp'
209 # See that the file appears empty again
210 $QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
212 echo
213 echo '=== Writable export ==='
215 fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
217 # Check that writing to the read-only export fails
218 $QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" | _filter_qemu_io
220 # But here it should work
221 $QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
223 # (Adjust the copy, too)
224 $QEMU_IO -f raw -c 'write -P 42 1M 64k' "$COPIED_IMG" | _filter_qemu_io
226 echo
227 echo '=== Resizing exports ==='
229 # Here, we need to export the protocol node -- the format layer may
230 # not be growable, simply because the format does not support it.
232 # Remove all exports and the format node first so permissions will not
233 # get in the way
234 fuse_export_del 'export-mp'
235 fuse_export_del 'export-img'
237 _send_qemu_cmd $QEMU_HANDLE \
238 "{'execute': 'blockdev-del',
239 'arguments': {
240 'node-name': 'node-format'
241 } }" \
242 'return'
244 # Now export the protocol node
245 fuse_export_add \
246 'export-mp' \
247 "'mountpoint': '$EXT_MP', 'writable': true" \
248 'return' \
249 'node-protocol'
251 echo
252 echo '--- Try growing non-growable export ---'
254 # Get the current size so we can write beyond the EOF
255 orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
256 orig_disk_usage=$(stat -c '%b' "$TEST_IMG")
258 # Should fail (exports are non-growable by default)
259 # (Note that qemu-io can never write beyond the EOF, so we have to use
260 # dd here)
261 dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$orig_len 2>&1 \
262 | _filter_testdir | _filter_imgfmt
264 echo
265 echo '--- Resize export ---'
267 # But we can truncate it explicitly; even with fallocate
268 fallocate -o "$orig_len" -l 64k "$EXT_MP"
270 new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
271 if [ "$new_len" != "$((orig_len + 65536))" ]; then
272 echo 'ERROR: Unexpected post-truncate image size:'
273 echo "$new_len != $((orig_len + 65536))"
274 else
275 echo 'OK: Post-truncate image size is as expected'
278 new_disk_usage=$(stat -c '%b' "$TEST_IMG")
279 if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then
280 echo 'OK: Disk usage grew with fallocate'
281 else
282 echo 'ERROR: Disk usage did not grow despite fallocate:'
283 echo "$orig_disk_usage => $new_disk_usage"
286 echo
287 echo '--- Try growing growable export ---'
289 # Now export as growable
290 fuse_export_del 'export-mp'
291 fuse_export_add \
292 'export-mp' \
293 "'mountpoint': '$EXT_MP', 'writable': true, 'growable': true" \
294 'return' \
295 'node-protocol'
297 # Now we should be able to write beyond the EOF
298 dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$new_len 2>&1 \
299 | _filter_testdir | _filter_imgfmt
301 new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
302 if [ "$new_len" != "$((orig_len + 131072))" ]; then
303 echo 'ERROR: Unexpected post-grow image size:'
304 echo "$new_len != $((orig_len + 131072))"
305 else
306 echo 'OK: Post-grow image size is as expected'
309 echo
310 echo '--- Shrink export ---'
312 # Now go back to the original size
313 truncate -s "$orig_len" "$EXT_MP"
315 new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
316 if [ "$new_len" != "$orig_len" ]; then
317 echo 'ERROR: Unexpected post-truncate image size:'
318 echo "$new_len != $orig_len"
319 else
320 echo 'OK: Post-truncate image size is as expected'
323 echo
324 echo '=== Tear down ==='
326 _send_qemu_cmd $QEMU_HANDLE \
327 "{'execute': 'quit'}" \
328 'return'
330 wait=yes _cleanup_qemu
332 echo
333 echo '=== Compare copy with original ==='
335 $QEMU_IMG compare -f raw -F $IMGFMT "$COPIED_IMG" "$TEST_IMG"
337 # success, all done
338 echo "*** done"
339 rm -f $seq.full
340 status=0