| #!/usr/bin/env bash | 
 | # group: rw auto quick | 
 | # | 
 | # Test qemu-img convert --salvage | 
 | # | 
 | # Copyright (C) 2019 Red Hat, Inc. | 
 | # | 
 | # This program is free software; you can redistribute it and/or modify | 
 | # it under the terms of the GNU General Public License as published by | 
 | # the Free Software Foundation; either version 2 of the License, or | 
 | # (at your option) any later version. | 
 | # | 
 | # This program is distributed in the hope that it will be useful, | 
 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | # GNU General Public License for more details. | 
 | # | 
 | # You should have received a copy of the GNU General Public License | 
 | # along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
 | # | 
 |  | 
 | # creator | 
 | owner=hreitz@redhat.com | 
 |  | 
 | seq=$(basename $0) | 
 | echo "QA output created by $seq" | 
 |  | 
 | status=1	# failure is the default! | 
 |  | 
 | _cleanup() | 
 | { | 
 |     _cleanup_test_img | 
 | } | 
 | trap "_cleanup; exit \$status" 0 1 2 3 15 | 
 |  | 
 | # get standard environment, filters and checks | 
 | . ./common.rc | 
 | . ./common.filter | 
 | . ./common.qemu | 
 |  | 
 | _supported_fmt generic | 
 | _supported_proto file | 
 | _supported_os Linux | 
 | _unsupported_imgopts "subformat=streamOptimized" | 
 |  | 
 | if [ "$IMGOPTSSYNTAX" = "true" ]; then | 
 |     # We use json:{} filenames here, so we cannot work with additional options. | 
 |     _unsupported_fmt $IMGFMT | 
 | else | 
 |     # - With VDI, the output is ordered differently.  Just disable it. | 
 |     # - VHDX has large clusters; because qemu-img convert tries to | 
 |     #   align the requests to the cluster size, the output is ordered | 
 |     #   differently, so disable it, too. | 
 |     _unsupported_fmt vdi vhdx | 
 | fi | 
 |  | 
 |  | 
 | TEST_IMG="$TEST_IMG.orig" _make_test_img 64M | 
 |  | 
 | $QEMU_IO -c 'write -P 42 0 64M' "$TEST_IMG.orig" | _filter_qemu_io | 
 |  | 
 |  | 
 | sector_size=512 | 
 |  | 
 | # Offsets on which to fail block-status.  Keep in ascending order so | 
 | # the indexing done by _filter_offsets will appear in ascending order | 
 | # in the output as well. | 
 | status_fail_offsets="$((16 * 1024 * 1024 + 8192)) | 
 |                      $((33 * 1024 * 1024 + 512))" | 
 |  | 
 | # Offsets on which to fail reads.  Keep in ascending order for the | 
 | # same reason. | 
 | # The second element is shared with $status_fail_offsets on purpose. | 
 | # Starting with the third element, we test what happens when a | 
 | # continuous range of sectors is inaccessible. | 
 | read_fail_offsets="$((32 * 1024 * 1024 - 65536)) | 
 |                    $((33 * 1024 * 1024 + 512)) | 
 |                    $(seq $((34 * 1024 * 1024)) $sector_size \ | 
 |                          $((34 * 1024 * 1024 + 4096 - $sector_size)))" | 
 |  | 
 |  | 
 | # blkdebug must be above the format layer so it can intercept all | 
 | # block-status events | 
 | source_img="json:{'driver': 'blkdebug', | 
 |                   'image': { | 
 |                       'driver': '$IMGFMT', | 
 |                       'file': { | 
 |                           'driver': 'file', | 
 |                           'filename': '$TEST_IMG.orig' | 
 |                       } | 
 |                   }, | 
 |                   'inject-error': [" | 
 |  | 
 | for ofs in $status_fail_offsets | 
 | do | 
 |     source_img+="{ 'event': 'none', | 
 |                    'iotype': 'block-status', | 
 |                    'errno': 5, | 
 |                    'sector': $((ofs / sector_size)) }," | 
 | done | 
 |  | 
 | for ofs in $read_fail_offsets | 
 | do | 
 |     source_img+="{ 'event': 'none', | 
 |                    'iotype': 'read', | 
 |                    'errno': 5, | 
 |                    'sector': $((ofs / sector_size)) }," | 
 | done | 
 |  | 
 | # Remove the trailing comma and terminate @inject-error and json:{} | 
 | source_img="${source_img%,} ] }" | 
 |  | 
 |  | 
 | echo | 
 |  | 
 |  | 
 | _filter_offsets() { | 
 |     filters= | 
 |  | 
 |     index=0 | 
 |     for ofs in $1 | 
 |     do | 
 |         filters+=" -e s/$ofs/status_fail_offset_$index/" | 
 |         index=$((index + 1)) | 
 |     done | 
 |  | 
 |     index=0 | 
 |     for ofs in $2 | 
 |     do | 
 |         filters+=" -e s/$ofs/read_fail_offset_$index/" | 
 |         index=$((index + 1)) | 
 |     done | 
 |  | 
 |     sed $filters | 
 | } | 
 |  | 
 | # While determining the number of allocated sectors in the input | 
 | # image, we should see one block status warning per element of | 
 | # $status_fail_offsets. | 
 | # | 
 | # Then, the image is read.  Since the block status is queried in | 
 | # basically the same way, the same warnings as in the previous step | 
 | # should reappear.  Interleaved with those we should see a read | 
 | # warning per element of $read_fail_offsets. | 
 | # Note that $read_fail_offsets and $status_fail_offsets share an | 
 | # element (read_fail_offset_1 == status_fail_offset_1), so | 
 | # "status_fail_offset_1" in the output is the same as | 
 | # "read_fail_offset_1". | 
 | $QEMU_IMG convert --salvage "$source_img" "$TEST_IMG" 2>&1 \ | 
 |     | _filter_offsets "$status_fail_offsets" "$read_fail_offsets" | 
 |  | 
 | echo | 
 |  | 
 | # The offsets where the block status could not be determined should | 
 | # have been treated as containing data and thus should be correct in | 
 | # the output image. | 
 | # The offsets where reading failed altogether should be 0.  Make them | 
 | # 0 in the input image, too, so we can compare both images. | 
 | for ofs in $read_fail_offsets | 
 | do | 
 |     $QEMU_IO -c "write -z $ofs $sector_size" "$TEST_IMG.orig" \ | 
 |         | _filter_qemu_io \ | 
 |         | _filter_offsets '' "$read_fail_offsets" | 
 | done | 
 |  | 
 | echo | 
 |  | 
 | # These should be equal now. | 
 | $QEMU_IMG compare "$TEST_IMG.orig" "$TEST_IMG" | 
 |  | 
 |  | 
 | # success, all done | 
 | echo "*** done" | 
 | rm -f $seq.full | 
 | status=0 |