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