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
|
#!/usr/bin/env bash
#
# Test qcow2 image compression
#
# Copyright (C) 2018 Igalia, S.L.
# Author: Alberto Garcia <berto@igalia.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
seq=$(basename "$0")
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto file
# Repairing the corrupted image requires qemu-img check to store a
# refcount up to 3, which requires at least two refcount bits.
# External data files do not support compressed clusters.
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
echo
echo "=== Corrupted size field in compressed cluster descriptor ==="
echo
# Create an empty image and fill half of it with compressed data.
# The L2 entries of the two compressed clusters are located at
# 0x800000 and 0x800008, their original values are 0x4008000000a00000
# and 0x4008000000a00802 (5 sectors for compressed data each).
_make_test_img 8M -o cluster_size=2M
$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \
2>&1 | _filter_qemu_io | _filter_testdir
# Reduce size of compressed data to 4 sectors: this corrupts the image.
poke_file "$TEST_IMG" $((0x800000)) "\x40\x06"
$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
# 'qemu-img check' however doesn't see anything wrong because it
# doesn't try to decompress the data and the refcounts are consistent.
# TODO: update qemu-img so this can be detected.
_check_test_img
# Increase size of compressed data to the maximum (8192 sectors).
# This makes QEMU read more data (8192 sectors instead of 5, host
# addresses [0xa00000, 0xdfffff]), but the decompression algorithm
# stops once we have enough to restore the uncompressed cluster, so
# the rest of the data is ignored.
poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe"
# Do it also for the second compressed cluster (L2 entry at 0x800008).
# In this case the compressed data would span 3 host clusters
# (host addresses: [0xa00802, 0xe00801])
poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe"
# Here the image is too small so we're asking QEMU to read beyond the
# end of the image.
$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
# But if we grow the image we won't be reading beyond its end anymore.
$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
# The refcount data is however wrong because due to the increased size
# of the compressed data it now reaches the following host clusters.
# This can be repaired by qemu-img check by increasing the refcount of
# those clusters.
# TODO: update qemu-img to correct the compressed cluster size instead.
_check_test_img -r all
$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "=== Write compressed data of multiple clusters ==="
echo
cluster_size=0x10000
_make_test_img 2M -o cluster_size=$cluster_size
echo "Write uncompressed data:"
let data_size="8 * $cluster_size"
$QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \
2>&1 | _filter_qemu_io | _filter_testdir
sizeA=$($QEMU_IMG info --output=json "$TEST_IMG" |
sed -n '/"actual-size":/ s/[^0-9]//gp')
_make_test_img 2M -o cluster_size=$cluster_size
echo "Write compressed data:"
let data_size="3 * $cluster_size + $cluster_size / 2"
# Set compress on. That will align the written data
# by the cluster size and will write them compressed.
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \
$QEMU_IO -c "write -P 0xbb 0 $data_size" --image-opts \
"driver=compress,file.driver=$IMGFMT,file.file.driver=file,file.file.filename=$TEST_IMG" \
2>&1 | _filter_qemu_io | _filter_testdir
let offset="4 * $cluster_size + $cluster_size / 4"
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \
$QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\
'driver': 'compress',
'file': {'driver': '$IMGFMT',
'file': {'driver': 'file',
'filename': '$TEST_IMG'}}}" | \
_filter_qemu_io | _filter_testdir
sizeB=$($QEMU_IMG info --output=json "$TEST_IMG" |
sed -n '/"actual-size":/ s/[^0-9]//gp')
if [ $sizeA -lt $sizeB ]
then
echo "Compression ERROR ($sizeA < $sizeB)"
fi
$QEMU_IMG check --output=json "$TEST_IMG" |
sed -n 's/,$//; /"compressed-clusters":/ s/^ *//p'
# success, all done
echo '*** done'
rm -f $seq.full
status=0
|