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