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 2542a5009dSJohn 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:" 396*52df1a5bSAbhiram Tilakecho " qemu-img info reports $(_img_info | grep -c '^ \{30\}') 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:' 523*52df1a5bSAbhiram Tilakecho " qemu-img info reports $(_img_info | grep -c '^ \{30\}') 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