xref: /openbmc/qemu/tests/qemu-iotests/261 (revision 42a5009d)
1f53b25dfSMax Reitz#!/usr/bin/env bash
29dd003a9SVladimir Sementsov-Ogievskiy# group: rw
3f53b25dfSMax Reitz#
4f53b25dfSMax Reitz# Test case for qcow2's handling of extra data in snapshot table entries
5f53b25dfSMax Reitz# (and more generally, how certain cases of broken snapshot tables are
6f53b25dfSMax Reitz# handled)
7f53b25dfSMax Reitz#
8f53b25dfSMax Reitz# Copyright (C) 2019 Red Hat, Inc.
9f53b25dfSMax Reitz#
10f53b25dfSMax Reitz# This program is free software; you can redistribute it and/or modify
11f53b25dfSMax Reitz# it under the terms of the GNU General Public License as published by
12f53b25dfSMax Reitz# the Free Software Foundation; either version 2 of the License, or
13f53b25dfSMax Reitz# (at your option) any later version.
14f53b25dfSMax Reitz#
15f53b25dfSMax Reitz# This program is distributed in the hope that it will be useful,
16f53b25dfSMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of
17f53b25dfSMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18f53b25dfSMax Reitz# GNU General Public License for more details.
19f53b25dfSMax Reitz#
20f53b25dfSMax Reitz# You should have received a copy of the GNU General Public License
21f53b25dfSMax Reitz# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22f53b25dfSMax Reitz#
23f53b25dfSMax Reitz
24f53b25dfSMax Reitz# creator
25*42a5009dSJohn Snowowner=hreitz@redhat.com
26f53b25dfSMax Reitz
27f53b25dfSMax Reitzseq=$(basename $0)
28f53b25dfSMax Reitzecho "QA output created by $seq"
29f53b25dfSMax Reitz
30f53b25dfSMax Reitzstatus=1	# failure is the default!
31f53b25dfSMax Reitz
32f53b25dfSMax Reitz_cleanup()
33f53b25dfSMax Reitz{
34f53b25dfSMax Reitz    _cleanup_test_img
35f53b25dfSMax Reitz    rm -f "$TEST_IMG".v{2,3}.orig
36f53b25dfSMax Reitz    rm -f "$TEST_DIR"/sn{0,1,2}{,-pre,-extra,-post}
37f53b25dfSMax Reitz}
38f53b25dfSMax Reitztrap "_cleanup; exit \$status" 0 1 2 3 15
39f53b25dfSMax Reitz
40f53b25dfSMax Reitz# get standard environment, filters and checks
41f53b25dfSMax Reitz. ./common.rc
42f53b25dfSMax Reitz. ./common.filter
43f53b25dfSMax Reitz
44e696f335SMax Reitz# This tests qcow2-specific low-level functionality
45f53b25dfSMax Reitz_supported_fmt qcow2
46f53b25dfSMax Reitz_supported_proto file
47f53b25dfSMax Reitz_supported_os Linux
48f53b25dfSMax Reitz# (1) We create a v2 image that supports nothing but refcount_bits=16
49f53b25dfSMax Reitz# (2) We do some refcount management on our own which expects
50f53b25dfSMax Reitz#     refcount_bits=16
513be2024aSMax Reitz# As for data files, they do not support snapshots at all.
523be2024aSMax Reitz_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
53f53b25dfSMax Reitz
54f53b25dfSMax Reitz# Parameters:
55f53b25dfSMax Reitz#   $1: image filename
56f53b25dfSMax Reitz#   $2: snapshot table entry offset in the image
57f53b25dfSMax Reitzsnapshot_table_entry_size()
58f53b25dfSMax Reitz{
59f53b25dfSMax Reitz    id_len=$(peek_file_be "$1" $(($2 + 12)) 2)
60f53b25dfSMax Reitz    name_len=$(peek_file_be "$1" $(($2 + 14)) 2)
61f53b25dfSMax Reitz    extra_len=$(peek_file_be "$1" $(($2 + 36)) 4)
62f53b25dfSMax Reitz
63f53b25dfSMax Reitz    full_len=$((40 + extra_len + id_len + name_len))
64f53b25dfSMax Reitz    echo $(((full_len + 7) / 8 * 8))
65f53b25dfSMax Reitz}
66f53b25dfSMax Reitz
67f53b25dfSMax Reitz# Parameter:
68f53b25dfSMax Reitz#   $1: image filename
69f53b25dfSMax Reitzprint_snapshot_table()
70f53b25dfSMax Reitz{
71f53b25dfSMax Reitz    nb_entries=$(peek_file_be "$1" 60 4)
72f53b25dfSMax Reitz    offset=$(peek_file_be "$1" 64 8)
73f53b25dfSMax Reitz
74f53b25dfSMax Reitz    echo "Snapshots in $1:" | _filter_testdir | _filter_imgfmt
75f53b25dfSMax Reitz
76f53b25dfSMax Reitz    for ((i = 0; i < nb_entries; i++)); do
77f53b25dfSMax Reitz        id_len=$(peek_file_be "$1" $((offset + 12)) 2)
78f53b25dfSMax Reitz        name_len=$(peek_file_be "$1" $((offset + 14)) 2)
79f53b25dfSMax Reitz        extra_len=$(peek_file_be "$1" $((offset + 36)) 4)
80f53b25dfSMax Reitz
81f53b25dfSMax Reitz        extra_ofs=$((offset + 40))
82f53b25dfSMax Reitz        id_ofs=$((extra_ofs + extra_len))
83f53b25dfSMax Reitz        name_ofs=$((id_ofs + id_len))
84f53b25dfSMax Reitz
85f53b25dfSMax Reitz        echo "  [$i]"
86f53b25dfSMax Reitz        echo "    ID: $(peek_file_raw "$1" $id_ofs $id_len)"
87f53b25dfSMax Reitz        echo "    Name: $(peek_file_raw "$1" $name_ofs $name_len)"
88f53b25dfSMax Reitz        echo "    Extra data size: $extra_len"
89f53b25dfSMax Reitz        if [ $extra_len -ge 8 ]; then
90f53b25dfSMax Reitz            echo "    VM state size: $(peek_file_be "$1" $extra_ofs 8)"
91f53b25dfSMax Reitz        fi
92f53b25dfSMax Reitz        if [ $extra_len -ge 16 ]; then
93f53b25dfSMax Reitz            echo "    Disk size: $(peek_file_be "$1" $((extra_ofs + 8)) 8)"
94f53b25dfSMax Reitz        fi
95bbacffc5SPavel Dovgalyuk        if [ $extra_len -ge 24 ]; then
96bbacffc5SPavel Dovgalyuk            echo "    Icount: $(peek_file_be "$1" $((extra_ofs + 16)) 8)"
97bbacffc5SPavel Dovgalyuk        fi
98bbacffc5SPavel Dovgalyuk        if [ $extra_len -gt 24 ]; then
99f53b25dfSMax Reitz            echo '    Unknown extra data:' \
100f53b25dfSMax Reitz                "$(peek_file_raw "$1" $((extra_ofs + 16)) $((extra_len - 16)) \
101f53b25dfSMax Reitz                   | tr -d '\0')"
102f53b25dfSMax Reitz        fi
103f53b25dfSMax Reitz
104f53b25dfSMax Reitz        offset=$((offset + $(snapshot_table_entry_size "$1" $offset)))
105f53b25dfSMax Reitz    done
106f53b25dfSMax Reitz}
107f53b25dfSMax Reitz
108f53b25dfSMax Reitz# Mark clusters as allocated; works only in refblock 0 (i.e. before
109f53b25dfSMax Reitz# cluster #32768).
110f53b25dfSMax Reitz# Parameters:
111f53b25dfSMax Reitz#   $1: Start offset of what to allocate
112f53b25dfSMax Reitz#   $2: End offset (exclusive)
113f53b25dfSMax Reitzrefblock0_allocate()
114f53b25dfSMax Reitz{
115f53b25dfSMax Reitz    reftable_ofs=$(peek_file_be "$TEST_IMG" 48 8)
116f53b25dfSMax Reitz    refblock_ofs=$(peek_file_be "$TEST_IMG" $reftable_ofs 8)
117f53b25dfSMax Reitz
118f53b25dfSMax Reitz    cluster=$(($1 / 65536))
119f53b25dfSMax Reitz    ecluster=$((($2 + 65535) / 65536))
120f53b25dfSMax Reitz
121f53b25dfSMax Reitz    while [ $cluster -lt $ecluster ]; do
122f53b25dfSMax Reitz        if [ $cluster -ge 32768 ]; then
123f53b25dfSMax Reitz            echo "*** Abort: Cluster $cluster exceeds refblock 0 ***"
124f53b25dfSMax Reitz            exit 1
125f53b25dfSMax Reitz        fi
126f53b25dfSMax Reitz        poke_file "$TEST_IMG" $((refblock_ofs + cluster * 2)) '\x00\x01'
127f53b25dfSMax Reitz        cluster=$((cluster + 1))
128f53b25dfSMax Reitz    done
129f53b25dfSMax Reitz}
130f53b25dfSMax Reitz
131f53b25dfSMax Reitz
132f53b25dfSMax Reitzecho
133f53b25dfSMax Reitzecho '=== Create v2 template ==='
134f53b25dfSMax Reitzecho
135f53b25dfSMax Reitz
136f53b25dfSMax Reitz# Create v2 image with a snapshot table with three entries:
137f53b25dfSMax Reitz# [0]: No extra data (valid with v2, not valid with v3)
138f53b25dfSMax Reitz# [1]: Has extra data unknown to qemu
139f53b25dfSMax Reitz# [2]: Has the 64-bit VM state size, but not the disk size (again,
140f53b25dfSMax Reitz#      valid with v2, not valid with v3)
141f53b25dfSMax Reitz
142f53b25dfSMax ReitzTEST_IMG="$TEST_IMG.v2.orig" IMGOPTS='compat=0.10' _make_test_img 64M
143f53b25dfSMax Reitz$QEMU_IMG snapshot -c sn0 "$TEST_IMG.v2.orig"
144f53b25dfSMax Reitz$QEMU_IMG snapshot -c sn1 "$TEST_IMG.v2.orig"
145f53b25dfSMax Reitz$QEMU_IMG snapshot -c sn2 "$TEST_IMG.v2.orig"
146f53b25dfSMax Reitz
147f53b25dfSMax Reitz# Copy out all existing snapshot table entries
148f53b25dfSMax Reitzsn_table_ofs=$(peek_file_be "$TEST_IMG.v2.orig" 64 8)
149f53b25dfSMax Reitz
150f53b25dfSMax Reitz# ofs: Snapshot table entry offset
151f53b25dfSMax Reitz# eds: Extra data size
152f53b25dfSMax Reitz# ids: Name + ID size
153f53b25dfSMax Reitz# len: Total entry length
154f53b25dfSMax Reitzsn0_ofs=$sn_table_ofs
155f53b25dfSMax Reitzsn0_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 36)) 4)
156f53b25dfSMax Reitzsn0_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 12)) 2) +
157f53b25dfSMax Reitz           $(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 14)) 2)))
158f53b25dfSMax Reitzsn0_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn0_ofs)
159f53b25dfSMax Reitzsn1_ofs=$((sn0_ofs + sn0_len))
160f53b25dfSMax Reitzsn1_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 36)) 4)
161f53b25dfSMax Reitzsn1_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 12)) 2) +
162f53b25dfSMax Reitz           $(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 14)) 2)))
163f53b25dfSMax Reitzsn1_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn1_ofs)
164f53b25dfSMax Reitzsn2_ofs=$((sn1_ofs + sn1_len))
165f53b25dfSMax Reitzsn2_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 36)) 4)
166f53b25dfSMax Reitzsn2_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 12)) 2) +
167f53b25dfSMax Reitz           $(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 14)) 2)))
168f53b25dfSMax Reitzsn2_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn2_ofs)
169f53b25dfSMax Reitz
170f53b25dfSMax Reitz# Data before extra data
171f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-pre" bs=1 skip=$sn0_ofs count=40 \
172f53b25dfSMax Reitz    &> /dev/null
173f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-pre" bs=1 skip=$sn1_ofs count=40 \
174f53b25dfSMax Reitz    &> /dev/null
175f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-pre" bs=1 skip=$sn2_ofs count=40 \
176f53b25dfSMax Reitz    &> /dev/null
177f53b25dfSMax Reitz
178f53b25dfSMax Reitz# Extra data
179f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-extra" bs=1 \
180f53b25dfSMax Reitz    skip=$((sn0_ofs + 40)) count=$sn0_eds &> /dev/null
181f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-extra" bs=1 \
182f53b25dfSMax Reitz    skip=$((sn1_ofs + 40)) count=$sn1_eds &> /dev/null
183f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-extra" bs=1 \
184f53b25dfSMax Reitz    skip=$((sn2_ofs + 40)) count=$sn2_eds &> /dev/null
185f53b25dfSMax Reitz
186f53b25dfSMax Reitz# Data after extra data
187f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-post" bs=1 \
188f53b25dfSMax Reitz    skip=$((sn0_ofs + 40 + sn0_eds)) count=$sn0_ids \
189f53b25dfSMax Reitz    &> /dev/null
190f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-post" bs=1 \
191f53b25dfSMax Reitz    skip=$((sn1_ofs + 40 + sn1_eds)) count=$sn1_ids \
192f53b25dfSMax Reitz    &> /dev/null
193f53b25dfSMax Reitzdd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-post" bs=1 \
194f53b25dfSMax Reitz    skip=$((sn2_ofs + 40 + sn2_eds)) count=$sn2_ids \
195f53b25dfSMax Reitz    &> /dev/null
196f53b25dfSMax Reitz
197f53b25dfSMax Reitz# Amend them, one by one
198f53b25dfSMax Reitz# Set sn0's extra data size to 0
199f53b25dfSMax Reitzpoke_file "$TEST_DIR/sn0-pre" 36 '\x00\x00\x00\x00'
200f53b25dfSMax Reitztruncate -s 0 "$TEST_DIR/sn0-extra"
201f53b25dfSMax Reitz# Grow sn0-post to pad
202f53b25dfSMax Reitztruncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn0-pre") - 40)) \
203f53b25dfSMax Reitz    "$TEST_DIR/sn0-post"
204f53b25dfSMax Reitz
205bbacffc5SPavel Dovgalyuk# Set sn1's extra data size to 50
206bbacffc5SPavel Dovgalyukpoke_file "$TEST_DIR/sn1-pre" 36 '\x00\x00\x00\x32'
207bbacffc5SPavel Dovgalyuktruncate -s 50 "$TEST_DIR/sn1-extra"
208bbacffc5SPavel Dovgalyukpoke_file "$TEST_DIR/sn1-extra" 24 'very important data'
209f53b25dfSMax Reitz# Grow sn1-post to pad
210bbacffc5SPavel Dovgalyuktruncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn1-pre") - 90)) \
211f53b25dfSMax Reitz    "$TEST_DIR/sn1-post"
212f53b25dfSMax Reitz
213f53b25dfSMax Reitz# Set sn2's extra data size to 8
214f53b25dfSMax Reitzpoke_file "$TEST_DIR/sn2-pre" 36 '\x00\x00\x00\x08'
215f53b25dfSMax Reitztruncate -s 8 "$TEST_DIR/sn2-extra"
216f53b25dfSMax Reitz# Grow sn2-post to pad
217f53b25dfSMax Reitztruncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn2-pre") - 48)) \
218f53b25dfSMax Reitz    "$TEST_DIR/sn2-post"
219f53b25dfSMax Reitz
220f53b25dfSMax Reitz# Construct snapshot table
221f53b25dfSMax Reitzcat "$TEST_DIR"/sn0-{pre,extra,post} \
222f53b25dfSMax Reitz    "$TEST_DIR"/sn1-{pre,extra,post} \
223f53b25dfSMax Reitz    "$TEST_DIR"/sn2-{pre,extra,post} \
224f53b25dfSMax Reitz    | dd of="$TEST_IMG.v2.orig" bs=1 seek=$sn_table_ofs conv=notrunc \
225f53b25dfSMax Reitz          &> /dev/null
226f53b25dfSMax Reitz
227f53b25dfSMax Reitz# Done!
228f53b25dfSMax ReitzTEST_IMG="$TEST_IMG.v2.orig" _check_test_img
229f53b25dfSMax Reitzprint_snapshot_table "$TEST_IMG.v2.orig"
230f53b25dfSMax Reitz
231f53b25dfSMax Reitzecho
232f53b25dfSMax Reitzecho '=== Upgrade to v3 ==='
233f53b25dfSMax Reitzecho
234f53b25dfSMax Reitz
235f53b25dfSMax Reitzcp "$TEST_IMG.v2.orig" "$TEST_IMG.v3.orig"
236f53b25dfSMax Reitz$QEMU_IMG amend -o compat=1.1 "$TEST_IMG.v3.orig"
237f53b25dfSMax ReitzTEST_IMG="$TEST_IMG.v3.orig" _check_test_img
238f53b25dfSMax Reitzprint_snapshot_table "$TEST_IMG.v3.orig"
239f53b25dfSMax Reitz
240f53b25dfSMax Reitzecho
241f53b25dfSMax Reitzecho '=== Repair botched v3 ==='
242f53b25dfSMax Reitzecho
243f53b25dfSMax Reitz
244f53b25dfSMax Reitz# Force the v2 file to be v3.  v3 requires each snapshot table entry
245f53b25dfSMax Reitz# to have at least 16 bytes of extra data, so it will not comply to
246f53b25dfSMax Reitz# the qcow2 v3 specification; but we can fix that.
247f53b25dfSMax Reitzcp "$TEST_IMG.v2.orig" "$TEST_IMG"
248f53b25dfSMax Reitz
249f53b25dfSMax Reitz# Set version
250f53b25dfSMax Reitzpoke_file "$TEST_IMG" 4 '\x00\x00\x00\x03'
251f53b25dfSMax Reitz# Increase header length (necessary for v3)
252f53b25dfSMax Reitzpoke_file "$TEST_IMG" 100 '\x00\x00\x00\x68'
253f53b25dfSMax Reitz# Set refcount order (necessary for v3)
254f53b25dfSMax Reitzpoke_file "$TEST_IMG" 96 '\x00\x00\x00\x04'
255f53b25dfSMax Reitz
256f53b25dfSMax Reitz_check_test_img -r all
257f53b25dfSMax Reitzprint_snapshot_table "$TEST_IMG"
258f53b25dfSMax Reitz
259f53b25dfSMax Reitz
260f53b25dfSMax Reitz# From now on, just test the qcow2 version we are supposed to test.
261f53b25dfSMax Reitz# (v3 by default, v2 by choice through $IMGOPTS.)
262f53b25dfSMax Reitz# That works because we always write all known extra data when
263f53b25dfSMax Reitz# updating the snapshot table, independent of the version.
264f53b25dfSMax Reitz
265f53b25dfSMax Reitzif echo "$IMGOPTS" | grep -q 'compat=\(0\.10\|v2\)' 2> /dev/null; then
266f53b25dfSMax Reitz    subver=v2
267f53b25dfSMax Reitzelse
268f53b25dfSMax Reitz    subver=v3
269f53b25dfSMax Reitzfi
270f53b25dfSMax Reitz
271f53b25dfSMax Reitzecho
272f53b25dfSMax Reitzecho '=== Add new snapshot ==='
273f53b25dfSMax Reitzecho
274f53b25dfSMax Reitz
275f53b25dfSMax Reitzcp "$TEST_IMG.$subver.orig" "$TEST_IMG"
276f53b25dfSMax Reitz$QEMU_IMG snapshot -c sn3 "$TEST_IMG"
277f53b25dfSMax Reitz_check_test_img
278f53b25dfSMax Reitzprint_snapshot_table "$TEST_IMG"
279f53b25dfSMax Reitz
280f53b25dfSMax Reitzecho
281f53b25dfSMax Reitzecho '=== Remove different snapshots ==='
282f53b25dfSMax Reitz
283f53b25dfSMax Reitzfor sn in sn0 sn1 sn2; do
284f53b25dfSMax Reitz    echo
285f53b25dfSMax Reitz    echo "--- $sn ---"
286f53b25dfSMax Reitz
287f53b25dfSMax Reitz    cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
288f53b25dfSMax Reitz    $QEMU_IMG snapshot -d $sn "$TEST_IMG"
289f53b25dfSMax Reitz    _check_test_img
290f53b25dfSMax Reitz    print_snapshot_table "$TEST_IMG"
291f53b25dfSMax Reitzdone
292f53b25dfSMax Reitz
293f53b25dfSMax Reitzecho
294f53b25dfSMax Reitzecho '=== Reject too much unknown extra data ==='
295f53b25dfSMax Reitzecho
296f53b25dfSMax Reitz
297f53b25dfSMax Reitzcp "$TEST_IMG.$subver.orig" "$TEST_IMG"
298f53b25dfSMax Reitz$QEMU_IMG snapshot -c sn3 "$TEST_IMG"
299f53b25dfSMax Reitz
300f53b25dfSMax Reitzsn_table_ofs=$(peek_file_be "$TEST_IMG" 64 8)
301f53b25dfSMax Reitzsn0_ofs=$sn_table_ofs
302f53b25dfSMax Reitzsn1_ofs=$((sn0_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn0_ofs)))
303f53b25dfSMax Reitzsn2_ofs=$((sn1_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn1_ofs)))
304f53b25dfSMax Reitzsn3_ofs=$((sn2_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn2_ofs)))
305f53b25dfSMax Reitz
306f53b25dfSMax Reitz# 64 kB of extra data should be rejected
307f53b25dfSMax Reitz# (Note that this also induces a refcount error, because it spills
308f53b25dfSMax Reitz# over to the next cluster.  That's a good way to test that we can
309f53b25dfSMax Reitz# handle simultaneous snapshot table and refcount errors.)
310f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((sn3_ofs + 36)) '\x00\x01\x00\x00'
311f53b25dfSMax Reitz
312f53b25dfSMax Reitz# Print error
313f53b25dfSMax Reitz_img_info
314f53b25dfSMax Reitzecho
315f53b25dfSMax Reitz_check_test_img
316f53b25dfSMax Reitzecho
317f53b25dfSMax Reitz
318f53b25dfSMax Reitz# Should be repairable
319f53b25dfSMax Reitz_check_test_img -r all
320f53b25dfSMax Reitz
321f53b25dfSMax Reitzecho
322f53b25dfSMax Reitzecho '=== Snapshot table too big ==='
323f53b25dfSMax Reitzecho
324f53b25dfSMax Reitz
325f53b25dfSMax Reitzsn_table_ofs=$(peek_file_be "$TEST_IMG.v3.orig" 64 8)
326f53b25dfSMax Reitz
327f53b25dfSMax Reitz# Fill a snapshot with 1 kB of extra data, a 65535-char ID, and a
328f53b25dfSMax Reitz# 65535-char name, and repeat it as many times as necessary to fill
329f53b25dfSMax Reitz# 64 MB (the maximum supported by qemu)
330f53b25dfSMax Reitz
331f53b25dfSMax Reitztouch "$TEST_DIR/sn0"
332f53b25dfSMax Reitz
333f53b25dfSMax Reitz# Full size (fixed + extra + ID + name + padding)
334f53b25dfSMax Reitzsn_size=$((40 + 1024 + 65535 + 65535 + 2))
335f53b25dfSMax Reitz
336f53b25dfSMax Reitz# We only need the fixed part, though.
337f53b25dfSMax Reitztruncate -s 40 "$TEST_DIR/sn0"
338f53b25dfSMax Reitz
339f53b25dfSMax Reitz# 65535-char ID string
340f53b25dfSMax Reitzpoke_file "$TEST_DIR/sn0" 12 '\xff\xff'
341f53b25dfSMax Reitz# 65535-char name
342f53b25dfSMax Reitzpoke_file "$TEST_DIR/sn0" 14 '\xff\xff'
343f53b25dfSMax Reitz# 1 kB of extra data
344f53b25dfSMax Reitzpoke_file "$TEST_DIR/sn0" 36 '\x00\x00\x04\x00'
345f53b25dfSMax Reitz
346f53b25dfSMax Reitz# Create test image
347f53b25dfSMax Reitz_make_test_img 64M
348f53b25dfSMax Reitz
349f53b25dfSMax Reitz# Hook up snapshot table somewhere safe (at 1 MB)
350f53b25dfSMax Reitzpoke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
351f53b25dfSMax Reitz
352f53b25dfSMax Reitzoffset=1048576
353f53b25dfSMax Reitzsize_written=0
354f53b25dfSMax Reitzsn_count=0
355f53b25dfSMax Reitzwhile [ $size_written -le $((64 * 1048576)) ]; do
356f53b25dfSMax Reitz    dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
357f53b25dfSMax Reitz        &> /dev/null
358f53b25dfSMax Reitz    offset=$((offset + sn_size))
359f53b25dfSMax Reitz    size_written=$((size_written + sn_size))
360f53b25dfSMax Reitz    sn_count=$((sn_count + 1))
361f53b25dfSMax Reitzdone
362f53b25dfSMax Reitztruncate -s "$offset" "$TEST_IMG"
363f53b25dfSMax Reitz
364f53b25dfSMax Reitz# Give the last snapshot (the one to be removed) an L1 table so we can
365f53b25dfSMax Reitz# see how that is handled when repairing the image
366f53b25dfSMax Reitz# (Put it two clusters before 1 MB, and one L2 table one cluster
367f53b25dfSMax Reitz# before 1 MB)
368f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((offset - sn_size + 0)) \
369f53b25dfSMax Reitz    '\x00\x00\x00\x00\x00\x0e\x00\x00'
370f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((offset - sn_size + 8)) \
371f53b25dfSMax Reitz    '\x00\x00\x00\x01'
372f53b25dfSMax Reitz
373f53b25dfSMax Reitz# Hook up the L2 table
374f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
375f53b25dfSMax Reitz    '\x80\x00\x00\x00\x00\x0f\x00\x00'
376f53b25dfSMax Reitz
377f53b25dfSMax Reitz# Make sure all of the clusters we just hooked up are allocated:
378f53b25dfSMax Reitz# - The snapshot table
379f53b25dfSMax Reitz# - The last snapshot's L1 and L2 table
380f53b25dfSMax Reitzrefblock0_allocate $((1048576 - 2 * 65536)) $offset
381f53b25dfSMax Reitz
382f53b25dfSMax Reitzpoke_file "$TEST_IMG" 60 \
383f53b25dfSMax Reitz    "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
384f53b25dfSMax Reitz
385f53b25dfSMax Reitz# Print error
386f53b25dfSMax Reitz_img_info
387f53b25dfSMax Reitzecho
388f53b25dfSMax Reitz_check_test_img
389f53b25dfSMax Reitzecho
390f53b25dfSMax Reitz
391f53b25dfSMax Reitz# Should be repairable
392f53b25dfSMax Reitz_check_test_img -r all
393f53b25dfSMax Reitz
394f53b25dfSMax Reitzecho
395f53b25dfSMax Reitzecho "$((sn_count - 1)) snapshots should remain:"
396b39847a5SPavel Dovgalyukecho "  qemu-img info reports $(_img_info | grep -c '^ \{32\}') snapshots"
397f53b25dfSMax Reitzecho "  Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
398f53b25dfSMax Reitz
399f53b25dfSMax Reitzecho
400f53b25dfSMax Reitzecho '=== Snapshot table too big with one entry with too much extra data ==='
401f53b25dfSMax Reitzecho
402f53b25dfSMax Reitz
403f53b25dfSMax Reitz# For this test, we reuse the image from the previous case, which has
404f53b25dfSMax Reitz# a snapshot table that is right at the limit.
405f53b25dfSMax Reitz# Our layout looks like this:
406f53b25dfSMax Reitz# - (a number of snapshot table entries)
407f53b25dfSMax Reitz# - One snapshot with $extra_data_size extra data
408f53b25dfSMax Reitz# - One normal snapshot that breaks the 64 MB boundary
409f53b25dfSMax Reitz# - One normal snapshot beyond the 64 MB boundary
410f53b25dfSMax Reitz#
411f53b25dfSMax Reitz# $extra_data_size is calculated so that simply by virtue of it
412f53b25dfSMax Reitz# decreasing to 1 kB, the penultimate snapshot will fit into 64 MB
413f53b25dfSMax Reitz# limit again.  The final snapshot will always be beyond the limit, so
414f53b25dfSMax Reitz# that we can see that the repair algorithm does still determine the
415f53b25dfSMax Reitz# limit to be somewhere, even when truncating one snapshot's extra
416f53b25dfSMax Reitz# data.
417f53b25dfSMax Reitz
418f53b25dfSMax Reitz# The last case has removed the last snapshot, so calculate
419f53b25dfSMax Reitz# $old_offset to get the current image's real length
420f53b25dfSMax Reitzold_offset=$((offset - sn_size))
421f53b25dfSMax Reitz
422f53b25dfSMax Reitz# The layout from the previous test had one snapshot beyond the 64 MB
423f53b25dfSMax Reitz# limit; we want the same (after the oversized extra data has been
424f53b25dfSMax Reitz# truncated to 1 kB), so we drop the last three snapshots and
425f53b25dfSMax Reitz# construct them from scratch.
426f53b25dfSMax Reitzoffset=$((offset - 3 * sn_size))
427f53b25dfSMax Reitzsn_count=$((sn_count - 3))
428f53b25dfSMax Reitz
429f53b25dfSMax Reitz# Assuming we had already written one of the three snapshots
430f53b25dfSMax Reitz# (necessary so we can calculate $extra_data_size next).
431f53b25dfSMax Reitzsize_written=$((size_written - 2 * sn_size))
432f53b25dfSMax Reitz
433f53b25dfSMax Reitz# Increase the extra data size so we go past the limit
434f53b25dfSMax Reitz# (The -1024 comes from the 1 kB of extra data we already have)
435f53b25dfSMax Reitzextra_data_size=$((64 * 1048576 + 8 - sn_size - (size_written - 1024)))
436f53b25dfSMax Reitz
437f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((offset + 36)) \
438f53b25dfSMax Reitz    "$(printf '%08x' $extra_data_size | sed -e 's/\(..\)/\\x\1/g')"
439f53b25dfSMax Reitz
440f53b25dfSMax Reitzoffset=$((offset + sn_size - 1024 + extra_data_size))
441f53b25dfSMax Reitzsize_written=$((size_written - 1024 + extra_data_size))
442f53b25dfSMax Reitzsn_count=$((sn_count + 1))
443f53b25dfSMax Reitz
444f53b25dfSMax Reitz# Write the two normal snapshots
445f53b25dfSMax Reitzfor ((i = 0; i < 2; i++)); do
446f53b25dfSMax Reitz    dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
447f53b25dfSMax Reitz        &> /dev/null
448f53b25dfSMax Reitz    offset=$((offset + sn_size))
449f53b25dfSMax Reitz    size_written=$((size_written + sn_size))
450f53b25dfSMax Reitz    sn_count=$((sn_count + 1))
451f53b25dfSMax Reitz
452f53b25dfSMax Reitz    if [ $i = 0 ]; then
453f53b25dfSMax Reitz        # Check that the penultimate snapshot is beyond the 64 MB limit
454f53b25dfSMax Reitz        echo "Snapshot table size should equal $((64 * 1048576 + 8)):" \
455f53b25dfSMax Reitz            $size_written
456f53b25dfSMax Reitz        echo
457f53b25dfSMax Reitz    fi
458f53b25dfSMax Reitzdone
459f53b25dfSMax Reitz
460f53b25dfSMax Reitztruncate -s $offset "$TEST_IMG"
461f53b25dfSMax Reitzrefblock0_allocate $old_offset $offset
462f53b25dfSMax Reitz
463f53b25dfSMax Reitzpoke_file "$TEST_IMG" 60 \
464f53b25dfSMax Reitz    "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
465f53b25dfSMax Reitz
466f53b25dfSMax Reitz# Print error
467f53b25dfSMax Reitz_img_info
468f53b25dfSMax Reitzecho
469f53b25dfSMax Reitz_check_test_img
470f53b25dfSMax Reitzecho
471f53b25dfSMax Reitz
472f53b25dfSMax Reitz# Just truncating the extra data should be sufficient to shorten the
473f53b25dfSMax Reitz# snapshot table so only one snapshot exceeds the extra size
474f53b25dfSMax Reitz_check_test_img -r all
475f53b25dfSMax Reitz
476f53b25dfSMax Reitzecho
477f53b25dfSMax Reitzecho '=== Too many snapshots ==='
478f53b25dfSMax Reitzecho
479f53b25dfSMax Reitz
480f53b25dfSMax Reitz# Create a v2 image, for speeds' sake: All-zero snapshot table entries
481f53b25dfSMax Reitz# are only valid in v2.
482f53b25dfSMax ReitzIMGOPTS='compat=0.10' _make_test_img 64M
483f53b25dfSMax Reitz
484f53b25dfSMax Reitz# Hook up snapshot table somewhere safe (at 1 MB)
485f53b25dfSMax Reitzpoke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
486f53b25dfSMax Reitz# "Create" more than 65536 snapshots (twice that many here)
487f53b25dfSMax Reitzpoke_file "$TEST_IMG" 60 '\x00\x02\x00\x00'
488f53b25dfSMax Reitz
489f53b25dfSMax Reitz# 40-byte all-zero snapshot table entries are valid snapshots, but
490f53b25dfSMax Reitz# only in v2 (v3 needs 16 bytes of extra data, so we would have to
491f53b25dfSMax Reitz# write 131072x '\x10').
492f53b25dfSMax Reitztruncate -s $((1048576 + 40 * 131072)) "$TEST_IMG"
493f53b25dfSMax Reitz
494f53b25dfSMax Reitz# But let us give one of the snapshots to be removed an L1 table so
495f53b25dfSMax Reitz# we can see how that is handled when repairing the image.
496f53b25dfSMax Reitz# (Put it two clusters before 1 MB, and one L2 table one cluster
497f53b25dfSMax Reitz# before 1 MB)
498f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 0)) \
499f53b25dfSMax Reitz    '\x00\x00\x00\x00\x00\x0e\x00\x00'
500f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 8)) \
501f53b25dfSMax Reitz    '\x00\x00\x00\x01'
502f53b25dfSMax Reitz
503f53b25dfSMax Reitz# Hook up the L2 table
504f53b25dfSMax Reitzpoke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
505f53b25dfSMax Reitz    '\x80\x00\x00\x00\x00\x0f\x00\x00'
506f53b25dfSMax Reitz
507f53b25dfSMax Reitz# Make sure all of the clusters we just hooked up are allocated:
508f53b25dfSMax Reitz# - The snapshot table
509f53b25dfSMax Reitz# - The last snapshot's L1 and L2 table
510f53b25dfSMax Reitzrefblock0_allocate $((1048576 - 2 * 65536)) $((1048576 + 40 * 131072))
511f53b25dfSMax Reitz
512f53b25dfSMax Reitz# Print error
513f53b25dfSMax Reitz_img_info
514f53b25dfSMax Reitzecho
515f53b25dfSMax Reitz_check_test_img
516f53b25dfSMax Reitzecho
517f53b25dfSMax Reitz
518f53b25dfSMax Reitz# Should be repairable
519f53b25dfSMax Reitz_check_test_img -r all
520f53b25dfSMax Reitz
521f53b25dfSMax Reitzecho
522f53b25dfSMax Reitzecho '65536 snapshots should remain:'
523b39847a5SPavel Dovgalyukecho "  qemu-img info reports $(_img_info | grep -c '^ \{32\}') snapshots"
524f53b25dfSMax Reitzecho "  Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
525f53b25dfSMax Reitz
526f53b25dfSMax Reitz# success, all done
527f53b25dfSMax Reitzecho "*** done"
528f53b25dfSMax Reitzstatus=0
529