111a82d14SPhilippe Mathieu-Daudé#!/usr/bin/env bash 26cba5377SMax Reitz# 36cba5377SMax Reitz# Test qcow2 image compression 46cba5377SMax Reitz# 56cba5377SMax Reitz# Copyright (C) 2018 Igalia, S.L. 66cba5377SMax Reitz# Author: Alberto Garcia <berto@igalia.com> 76cba5377SMax Reitz# 86cba5377SMax Reitz# This program is free software; you can redistribute it and/or modify 96cba5377SMax Reitz# it under the terms of the GNU General Public License as published by 106cba5377SMax Reitz# the Free Software Foundation; either version 2 of the License, or 116cba5377SMax Reitz# (at your option) any later version. 126cba5377SMax Reitz# 136cba5377SMax Reitz# This program is distributed in the hope that it will be useful, 146cba5377SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 156cba5377SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 166cba5377SMax Reitz# GNU General Public License for more details. 176cba5377SMax Reitz# 186cba5377SMax Reitz# You should have received a copy of the GNU General Public License 196cba5377SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 206cba5377SMax Reitz# 216cba5377SMax Reitz 226cba5377SMax Reitzseq=$(basename "$0") 236cba5377SMax Reitzecho "QA output created by $seq" 246cba5377SMax Reitz 256cba5377SMax Reitzstatus=1 # failure is the default! 266cba5377SMax Reitz 276cba5377SMax Reitz_cleanup() 286cba5377SMax Reitz{ 296cba5377SMax Reitz _cleanup_test_img 306cba5377SMax Reitz} 316cba5377SMax Reitztrap "_cleanup; exit \$status" 0 1 2 3 15 326cba5377SMax Reitz 336cba5377SMax Reitz# get standard environment, filters and checks 346cba5377SMax Reitz. ./common.rc 356cba5377SMax Reitz. ./common.filter 366cba5377SMax Reitz 376cba5377SMax Reitz_supported_fmt qcow2 386cba5377SMax Reitz_supported_proto file 396cba5377SMax Reitz 406cba5377SMax Reitz# Repairing the corrupted image requires qemu-img check to store a 416cba5377SMax Reitz# refcount up to 3, which requires at least two refcount bits. 423be2024aSMax Reitz# External data files do not support compressed clusters. 433be2024aSMax Reitz_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file 446cba5377SMax Reitz 456cba5377SMax Reitz 466cba5377SMax Reitzecho 476cba5377SMax Reitzecho "=== Corrupted size field in compressed cluster descriptor ===" 486cba5377SMax Reitzecho 496cba5377SMax Reitz# Create an empty image and fill half of it with compressed data. 506cba5377SMax Reitz# The L2 entries of the two compressed clusters are located at 516cba5377SMax Reitz# 0x800000 and 0x800008, their original values are 0x4008000000a00000 526cba5377SMax Reitz# and 0x4008000000a00802 (5 sectors for compressed data each). 536cba5377SMax Reitz_make_test_img 8M -o cluster_size=2M 546cba5377SMax Reitz$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ 556cba5377SMax Reitz 2>&1 | _filter_qemu_io | _filter_testdir 566cba5377SMax Reitz 576cba5377SMax Reitz# Reduce size of compressed data to 4 sectors: this corrupts the image. 586cba5377SMax Reitzpoke_file "$TEST_IMG" $((0x800000)) "\x40\x06" 596cba5377SMax Reitz$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 606cba5377SMax Reitz 616cba5377SMax Reitz# 'qemu-img check' however doesn't see anything wrong because it 626cba5377SMax Reitz# doesn't try to decompress the data and the refcounts are consistent. 636cba5377SMax Reitz# TODO: update qemu-img so this can be detected. 646cba5377SMax Reitz_check_test_img 656cba5377SMax Reitz 666cba5377SMax Reitz# Increase size of compressed data to the maximum (8192 sectors). 676cba5377SMax Reitz# This makes QEMU read more data (8192 sectors instead of 5, host 686cba5377SMax Reitz# addresses [0xa00000, 0xdfffff]), but the decompression algorithm 696cba5377SMax Reitz# stops once we have enough to restore the uncompressed cluster, so 706cba5377SMax Reitz# the rest of the data is ignored. 716cba5377SMax Reitzpoke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" 726cba5377SMax Reitz# Do it also for the second compressed cluster (L2 entry at 0x800008). 736cba5377SMax Reitz# In this case the compressed data would span 3 host clusters 746cba5377SMax Reitz# (host addresses: [0xa00802, 0xe00801]) 756cba5377SMax Reitzpoke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" 766cba5377SMax Reitz 776cba5377SMax Reitz# Here the image is too small so we're asking QEMU to read beyond the 786cba5377SMax Reitz# end of the image. 796cba5377SMax Reitz$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 806cba5377SMax Reitz# But if we grow the image we won't be reading beyond its end anymore. 816cba5377SMax Reitz$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 826cba5377SMax Reitz$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 836cba5377SMax Reitz 846cba5377SMax Reitz# The refcount data is however wrong because due to the increased size 856cba5377SMax Reitz# of the compressed data it now reaches the following host clusters. 866cba5377SMax Reitz# This can be repaired by qemu-img check by increasing the refcount of 876cba5377SMax Reitz# those clusters. 886cba5377SMax Reitz# TODO: update qemu-img to correct the compressed cluster size instead. 896cba5377SMax Reitz_check_test_img -r all 906cba5377SMax Reitz$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 916cba5377SMax Reitz$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir 926cba5377SMax Reitz 93*19959445SAndrey Shinkevichecho 94*19959445SAndrey Shinkevichecho "=== Write compressed data of multiple clusters ===" 95*19959445SAndrey Shinkevichecho 96*19959445SAndrey Shinkevichcluster_size=0x10000 97*19959445SAndrey Shinkevich_make_test_img 2M -o cluster_size=$cluster_size 98*19959445SAndrey Shinkevich 99*19959445SAndrey Shinkevichecho "Write uncompressed data:" 100*19959445SAndrey Shinkevichlet data_size="8 * $cluster_size" 101*19959445SAndrey Shinkevich$QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \ 102*19959445SAndrey Shinkevich 2>&1 | _filter_qemu_io | _filter_testdir 103*19959445SAndrey ShinkevichsizeA=$($QEMU_IMG info --output=json "$TEST_IMG" | 104*19959445SAndrey Shinkevich sed -n '/"actual-size":/ s/[^0-9]//gp') 105*19959445SAndrey Shinkevich 106*19959445SAndrey Shinkevich_make_test_img 2M -o cluster_size=$cluster_size 107*19959445SAndrey Shinkevichecho "Write compressed data:" 108*19959445SAndrey Shinkevichlet data_size="3 * $cluster_size + $cluster_size / 2" 109*19959445SAndrey Shinkevich# Set compress on. That will align the written data 110*19959445SAndrey Shinkevich# by the cluster size and will write them compressed. 111*19959445SAndrey ShinkevichQEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ 112*19959445SAndrey Shinkevich$QEMU_IO -c "write -P 0xbb 0 $data_size" --image-opts \ 113*19959445SAndrey Shinkevich "driver=compress,file.driver=$IMGFMT,file.file.driver=file,file.file.filename=$TEST_IMG" \ 114*19959445SAndrey Shinkevich 2>&1 | _filter_qemu_io | _filter_testdir 115*19959445SAndrey Shinkevich 116*19959445SAndrey Shinkevichlet offset="4 * $cluster_size + $cluster_size / 4" 117*19959445SAndrey ShinkevichQEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ 118*19959445SAndrey Shinkevich$QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\ 119*19959445SAndrey Shinkevich 'driver': 'compress', 120*19959445SAndrey Shinkevich 'file': {'driver': '$IMGFMT', 121*19959445SAndrey Shinkevich 'file': {'driver': 'file', 122*19959445SAndrey Shinkevich 'filename': '$TEST_IMG'}}}" | \ 123*19959445SAndrey Shinkevich _filter_qemu_io | _filter_testdir 124*19959445SAndrey Shinkevich 125*19959445SAndrey ShinkevichsizeB=$($QEMU_IMG info --output=json "$TEST_IMG" | 126*19959445SAndrey Shinkevich sed -n '/"actual-size":/ s/[^0-9]//gp') 127*19959445SAndrey Shinkevich 128*19959445SAndrey Shinkevichif [ $sizeA -le $sizeB ] 129*19959445SAndrey Shinkevichthen 130*19959445SAndrey Shinkevich echo "Compression ERROR" 131*19959445SAndrey Shinkevichfi 132*19959445SAndrey Shinkevich 133*19959445SAndrey Shinkevich$QEMU_IMG check --output=json "$TEST_IMG" | 134*19959445SAndrey Shinkevich sed -n 's/,$//; /"compressed-clusters":/ s/^ *//p' 135*19959445SAndrey Shinkevich 1366cba5377SMax Reitz# success, all done 1376cba5377SMax Reitzecho '*** done' 1386cba5377SMax Reitzrm -f $seq.full 1396cba5377SMax Reitzstatus=0 140