Blame SOURCES/kvm-iotests-108-Test-new-refcount-rebuild-algorithm.patch

1be5c7
From d638552d76db0db9e2b6ae90a35f0b451b0cbaf8 Mon Sep 17 00:00:00 2001
1be5c7
From: Hanna Reitz <hreitz@redhat.com>
1be5c7
Date: Tue, 5 Apr 2022 15:46:51 +0200
1be5c7
Subject: [PATCH 4/6] iotests/108: Test new refcount rebuild algorithm
1be5c7
1be5c7
RH-Author: Hanna Reitz <hreitz@redhat.com>
1be5c7
RH-MergeRequest: 171: qcow2: Improve refcount structure rebuilding
1be5c7
RH-Commit: [2/4] 2aa8c383f0c88c414f10ade8bd2e8af07c35f35b
1be5c7
RH-Bugzilla: 1519071
1be5c7
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
1be5c7
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
1be5c7
RH-Acked-by: Eric Blake <eblake@redhat.com>
1be5c7
1be5c7
One clear problem with how qcow2's refcount structure rebuild algorithm
1be5c7
used to be before "qcow2: Improve refcount structure rebuilding" was
1be5c7
that it is prone to failure for qcow2 images on block devices: There is
1be5c7
generally unused space after the actual image, and if that exceeds what
1be5c7
one refblock covers, the old algorithm would invariably write the
1be5c7
reftable past the block device's end, which cannot work.  The new
1be5c7
algorithm does not have this problem.
1be5c7
1be5c7
Test it with three tests:
1be5c7
(1) Create an image with more empty space at the end than what one
1be5c7
    refblock covers, see whether rebuilding the refcount structures
1be5c7
    results in a change in the image file length.  (It should not.)
1be5c7
1be5c7
(2) Leave precisely enough space somewhere at the beginning of the image
1be5c7
    for the new reftable (and the refblock for that place), see whether
1be5c7
    the new algorithm puts the reftable there.  (It should.)
1be5c7
1be5c7
(3) Test the original problem: Create (something like) a block device
1be5c7
    with a fixed size, then create a qcow2 image in there, write some
1be5c7
    data, and then have qemu-img check rebuild the refcount structures.
1be5c7
    Before HEAD^, the reftable would have been written past the image
1be5c7
    file end, i.e. outside of what the block device provides, which
1be5c7
    cannot work.  HEAD^ should have fixed that.
1be5c7
    ("Something like a block device" means a loop device if we can use
1be5c7
    one ("sudo -n losetup" works), or a FUSE block export with
1be5c7
    growable=false otherwise.)
1be5c7
1be5c7
Reviewed-by: Eric Blake <eblake@redhat.com>
1be5c7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
1be5c7
Message-Id: <20220405134652.19278-3-hreitz@redhat.com>
1be5c7
(cherry picked from commit 9ffd6d646d1d5ee9087a8cbf0b7d2f96c5656162)
1be5c7
1be5c7
Conflicts:
1be5c7
- 108: The downstream qemu-storage-daemon does not support --daemonize,
1be5c7
  so this switch has been replaced by a loop waiting for the PID file to
1be5c7
  appear
1be5c7
1be5c7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
1be5c7
---
1be5c7
 tests/qemu-iotests/108     | 263 ++++++++++++++++++++++++++++++++++++-
1be5c7
 tests/qemu-iotests/108.out |  81 ++++++++++++
1be5c7
 2 files changed, 343 insertions(+), 1 deletion(-)
1be5c7
1be5c7
diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108
1be5c7
index 8eaef0b8bf..23abbeaff0 100755
1be5c7
--- a/tests/qemu-iotests/108
1be5c7
+++ b/tests/qemu-iotests/108
1be5c7
@@ -30,13 +30,20 @@ status=1	# failure is the default!
1be5c7
 
1be5c7
 _cleanup()
1be5c7
 {
1be5c7
-	_cleanup_test_img
1be5c7
+    _cleanup_test_img
1be5c7
+    if [ -f "$TEST_DIR/qsd.pid" ]; then
1be5c7
+        qsd_pid=$(cat "$TEST_DIR/qsd.pid")
1be5c7
+        kill -KILL "$qsd_pid"
1be5c7
+        fusermount -u "$TEST_DIR/fuse-export" &>/dev/null
1be5c7
+    fi
1be5c7
+    rm -f "$TEST_DIR/fuse-export"
1be5c7
 }
1be5c7
 trap "_cleanup; exit \$status" 0 1 2 3 15
1be5c7
 
1be5c7
 # get standard environment, filters and checks
1be5c7
 . ./common.rc
1be5c7
 . ./common.filter
1be5c7
+. ./common.qemu
1be5c7
 
1be5c7
 # This tests qcow2-specific low-level functionality
1be5c7
 _supported_fmt qcow2
1be5c7
@@ -47,6 +54,22 @@ _supported_os Linux
1be5c7
 # files
1be5c7
 _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
1be5c7
 
1be5c7
+# This test either needs sudo -n losetup or FUSE exports to work
1be5c7
+if sudo -n losetup &>/dev/null; then
1be5c7
+    loopdev=true
1be5c7
+else
1be5c7
+    loopdev=false
1be5c7
+
1be5c7
+    # QSD --export fuse will either yield "Parameter 'id' is missing"
1be5c7
+    # or "Invalid parameter 'fuse'", depending on whether there is
1be5c7
+    # FUSE support or not.
1be5c7
+    error=$($QSD --export fuse 2>&1)
1be5c7
+    if [[ $error = *"'fuse'"* ]]; then
1be5c7
+        _notrun 'Passwordless sudo for losetup or FUSE support required, but' \
1be5c7
+                'neither is available'
1be5c7
+    fi
1be5c7
+fi
1be5c7
+
1be5c7
 echo
1be5c7
 echo '=== Repairing an image without any refcount table ==='
1be5c7
 echo
1be5c7
@@ -138,6 +161,244 @@ _make_test_img 64M
1be5c7
 poke_file "$TEST_IMG" $((0x10008)) "\xff\xff\xff\xff\xff\xff\x00\x00"
1be5c7
 _check_test_img -r all
1be5c7
 
1be5c7
+echo
1be5c7
+echo '=== Check rebuilt reftable location ==='
1be5c7
+
1be5c7
+# In an earlier version of the refcount rebuild algorithm, the
1be5c7
+# reftable was generally placed at the image end (unless something was
1be5c7
+# allocated in the area covered by the refblock right before the image
1be5c7
+# file end, then we would try to place the reftable in that refblock).
1be5c7
+# This was later changed so the reftable would be placed in the
1be5c7
+# earliest possible location.  Test this.
1be5c7
+
1be5c7
+echo
1be5c7
+echo '--- Does the image size increase? ---'
1be5c7
+echo
1be5c7
+
1be5c7
+# First test: Just create some image, write some data to it, and
1be5c7
+# resize it so there is free space at the end of the image (enough
1be5c7
+# that it spans at least one full refblock, which for cluster_size=512
1be5c7
+# images, spans 128k).  With the old algorithm, the reftable would
1be5c7
+# have then been placed at the end of the image file, but with the new
1be5c7
+# one, it will be put in that free space.
1be5c7
+# We want to check whether the size of the image file increases due to
1be5c7
+# rebuilding the refcount structures (it should not).
1be5c7
+
1be5c7
+_make_test_img -o 'cluster_size=512' 1M
1be5c7
+# Write something
1be5c7
+$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
1be5c7
+
1be5c7
+# Add free space
1be5c7
+file_len=$(stat -c '%s' "$TEST_IMG")
1be5c7
+truncate -s $((file_len + 256 * 1024)) "$TEST_IMG"
1be5c7
+
1be5c7
+# Corrupt the image by saying the image header was not allocated
1be5c7
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
1be5c7
+rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8)
1be5c7
+poke_file "$TEST_IMG" $rb_offset "\x00\x00"
1be5c7
+
1be5c7
+# Check whether rebuilding the refcount structures increases the image
1be5c7
+# file size
1be5c7
+file_len=$(stat -c '%s' "$TEST_IMG")
1be5c7
+echo
1be5c7
+# The only leaks there can be are the old refcount structures that are
1be5c7
+# leaked during rebuilding, no need to clutter the output with them
1be5c7
+_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0'
1be5c7
+echo
1be5c7
+post_repair_file_len=$(stat -c '%s' "$TEST_IMG")
1be5c7
+
1be5c7
+if [[ $file_len -eq $post_repair_file_len ]]; then
1be5c7
+    echo 'OK: Image size did not change'
1be5c7
+else
1be5c7
+    echo 'ERROR: Image size differs' \
1be5c7
+        "($file_len before, $post_repair_file_len after)"
1be5c7
+fi
1be5c7
+
1be5c7
+echo
1be5c7
+echo '--- Will the reftable occupy a hole specifically left for it?  ---'
1be5c7
+echo
1be5c7
+
1be5c7
+# Note: With cluster_size=512, every refblock covers 128k.
1be5c7
+# The reftable covers 8M per reftable cluster.
1be5c7
+
1be5c7
+# Create an image that requires two reftable clusters (just because
1be5c7
+# this is more interesting than a single-clustered reftable).
1be5c7
+_make_test_img -o 'cluster_size=512' 9M
1be5c7
+$QEMU_IO -c 'write 0 8M' "$TEST_IMG" | _filter_qemu_io
1be5c7
+
1be5c7
+# Writing 8M will have resized the reftable.  Unfortunately, doing so
1be5c7
+# will leave holes in the file, so we need to fill them up so we can
1be5c7
+# be sure the whole file is allocated.  Do that by writing
1be5c7
+# consecutively smaller chunks starting from 8 MB, until the file
1be5c7
+# length increases even with a chunk size of 512.  Then we must have
1be5c7
+# filled all holes.
1be5c7
+ofs=$((8 * 1024 * 1024))
1be5c7
+block_len=$((16 * 1024))
1be5c7
+while [[ $block_len -ge 512 ]]; do
1be5c7
+    file_len=$(stat -c '%s' "$TEST_IMG")
1be5c7
+    while [[ $(stat -c '%s' "$TEST_IMG") -eq $file_len ]]; do
1be5c7
+        # Do not include this in the reference output, it does not
1be5c7
+        # really matter which qemu-io calls we do here exactly
1be5c7
+        $QEMU_IO -c "write $ofs $block_len" "$TEST_IMG" >/dev/null
1be5c7
+        ofs=$((ofs + block_len))
1be5c7
+    done
1be5c7
+    block_len=$((block_len / 2))
1be5c7
+done
1be5c7
+
1be5c7
+# Fill up to 9M (do not include this in the reference output either,
1be5c7
+# $ofs is random for all we know)
1be5c7
+$QEMU_IO -c "write $ofs $((9 * 1024 * 1024 - ofs))" "$TEST_IMG" >/dev/null
1be5c7
+
1be5c7
+# Make space as follows:
1be5c7
+# - For the first refblock: Right at the beginning of the image (this
1be5c7
+#   refblock is placed in the first place possible),
1be5c7
+# - For the reftable somewhere soon afterwards, still near the
1be5c7
+#   beginning of the image (i.e. covered by the first refblock); the
1be5c7
+#   reftable too is placed in the first place possible, but only after
1be5c7
+#   all refblocks have been placed)
1be5c7
+# No space is needed for the other refblocks, because no refblock is
1be5c7
+# put before the space it covers.  In this test case, we do not mind
1be5c7
+# if they are placed at the image file's end.
1be5c7
+
1be5c7
+# Before we make that space, we have to find out the host offset of
1be5c7
+# the area that belonged to the two data clusters at guest offset 4k,
1be5c7
+# because we expect the reftable to be placed there, and we will have
1be5c7
+# to verify that it is.
1be5c7
+
1be5c7
+l1_offset=$(peek_file_be "$TEST_IMG" 40 8)
1be5c7
+l2_offset=$(peek_file_be "$TEST_IMG" $l1_offset 8)
1be5c7
+l2_offset=$((l2_offset & 0x00fffffffffffe00))
1be5c7
+data_4k_offset=$(peek_file_be "$TEST_IMG" \
1be5c7
+                 $((l2_offset + 4096 / 512 * 8)) 8)
1be5c7
+data_4k_offset=$((data_4k_offset & 0x00fffffffffffe00))
1be5c7
+
1be5c7
+$QEMU_IO -c "discard 0 512" -c "discard 4k 1k" "$TEST_IMG" | _filter_qemu_io
1be5c7
+
1be5c7
+# Corrupt the image by saying the image header was not allocated
1be5c7
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
1be5c7
+rb_offset=$(peek_file_be "$TEST_IMG" $rt_offset 8)
1be5c7
+poke_file "$TEST_IMG" $rb_offset "\x00\x00"
1be5c7
+
1be5c7
+echo
1be5c7
+# The only leaks there can be are the old refcount structures that are
1be5c7
+# leaked during rebuilding, no need to clutter the output with them
1be5c7
+_check_test_img -r all | grep -v '^Repairing cluster.*refcount=1 reference=0'
1be5c7
+echo
1be5c7
+
1be5c7
+# Check whether the reftable was put where we expected
1be5c7
+rt_offset=$(peek_file_be "$TEST_IMG" 48 8)
1be5c7
+if [[ $rt_offset -eq $data_4k_offset ]]; then
1be5c7
+    echo 'OK: Reftable is where we expect it'
1be5c7
+else
1be5c7
+    echo "ERROR: Reftable is at $rt_offset, but was expected at $data_4k_offset"
1be5c7
+fi
1be5c7
+
1be5c7
+echo
1be5c7
+echo '--- Rebuilding refcount structures on block devices ---'
1be5c7
+echo
1be5c7
+
1be5c7
+# A block device cannot really grow, at least not during qemu-img
1be5c7
+# check.  As mentioned in the above cases, rebuilding the refcount
1be5c7
+# structure may lead to new refcount structures being written after
1be5c7
+# the end of the image, and in the past that happened even if there
1be5c7
+# was more than sufficient space in the image.  Such post-EOF writes
1be5c7
+# will not work on block devices, so test that the new algorithm
1be5c7
+# avoids it.
1be5c7
+
1be5c7
+# If we have passwordless sudo and losetup, we can use those to create
1be5c7
+# a block device.  Otherwise, we can resort to qemu's FUSE export to
1be5c7
+# create a file that isn't growable, which effectively tests the same
1be5c7
+# thing.
1be5c7
+
1be5c7
+_cleanup_test_img
1be5c7
+truncate -s $((64 * 1024 * 1024)) "$TEST_IMG"
1be5c7
+
1be5c7
+if $loopdev; then
1be5c7
+    export_mp=$(sudo -n losetup --show -f "$TEST_IMG")
1be5c7
+    export_mp_driver=host_device
1be5c7
+    sudo -n chmod go+rw "$export_mp"
1be5c7
+else
1be5c7
+    # Create non-growable FUSE export that is a bit like an empty
1be5c7
+    # block device
1be5c7
+    export_mp="$TEST_DIR/fuse-export"
1be5c7
+    export_mp_driver=file
1be5c7
+    touch "$export_mp"
1be5c7
+
1be5c7
+    $QSD \
1be5c7
+        --blockdev file,node-name=export-node,filename="$TEST_IMG" \
1be5c7
+        --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=off \
1be5c7
+        --pidfile "$TEST_DIR/qsd.pid" \
1be5c7
+        &
1be5c7
+
1be5c7
+    while [ ! -f "$TEST_DIR/qsd.pid" ]; do
1be5c7
+        sleep 0.1
1be5c7
+    done
1be5c7
+fi
1be5c7
+
1be5c7
+# Now create a qcow2 image on the device -- unfortunately, qemu-img
1be5c7
+# create force-creates the file, so we have to resort to the
1be5c7
+# blockdev-create job.
1be5c7
+_launch_qemu \
1be5c7
+    --blockdev $export_mp_driver,node-name=file,filename="$export_mp"
1be5c7
+
1be5c7
+_send_qemu_cmd \
1be5c7
+    $QEMU_HANDLE \
1be5c7
+    '{ "execute": "qmp_capabilities" }' \
1be5c7
+    'return'
1be5c7
+
1be5c7
+# Small cluster size again, so the image needs multiple refblocks
1be5c7
+_send_qemu_cmd \
1be5c7
+    $QEMU_HANDLE \
1be5c7
+    '{ "execute": "blockdev-create",
1be5c7
+       "arguments": {
1be5c7
+           "job-id": "create",
1be5c7
+           "options": {
1be5c7
+               "driver": "qcow2",
1be5c7
+               "file": "file",
1be5c7
+               "size": '$((64 * 1024 * 1024))',
1be5c7
+               "cluster-size": 512
1be5c7
+           } } }' \
1be5c7
+    '"concluded"'
1be5c7
+
1be5c7
+_send_qemu_cmd \
1be5c7
+    $QEMU_HANDLE \
1be5c7
+    '{ "execute": "job-dismiss", "arguments": { "id": "create" } }' \
1be5c7
+    'return'
1be5c7
+
1be5c7
+_send_qemu_cmd \
1be5c7
+    $QEMU_HANDLE \
1be5c7
+    '{ "execute": "quit" }' \
1be5c7
+    'return'
1be5c7
+
1be5c7
+wait=y _cleanup_qemu
1be5c7
+echo
1be5c7
+
1be5c7
+# Write some data
1be5c7
+$QEMU_IO -c 'write 0 64k' "$export_mp" | _filter_qemu_io
1be5c7
+
1be5c7
+# Corrupt the image by saying the image header was not allocated
1be5c7
+rt_offset=$(peek_file_be "$export_mp" 48 8)
1be5c7
+rb_offset=$(peek_file_be "$export_mp" $rt_offset 8)
1be5c7
+poke_file "$export_mp" $rb_offset "\x00\x00"
1be5c7
+
1be5c7
+# Repairing such a simple case should just work
1be5c7
+# (We used to put the reftable at the end of the image file, which can
1be5c7
+# never work for non-growable devices.)
1be5c7
+echo
1be5c7
+TEST_IMG="$export_mp" _check_test_img -r all \
1be5c7
+    | grep -v '^Repairing cluster.*refcount=1 reference=0'
1be5c7
+
1be5c7
+if $loopdev; then
1be5c7
+    sudo -n losetup -d "$export_mp"
1be5c7
+else
1be5c7
+    qsd_pid=$(cat "$TEST_DIR/qsd.pid")
1be5c7
+    kill -TERM "$qsd_pid"
1be5c7
+    # Wait for process to exit (cannot `wait` because the QSD is daemonized)
1be5c7
+    while [ -f "$TEST_DIR/qsd.pid" ]; do
1be5c7
+        true
1be5c7
+    done
1be5c7
+fi
1be5c7
+
1be5c7
 # success, all done
1be5c7
 echo '*** done'
1be5c7
 rm -f $seq.full
1be5c7
diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out
1be5c7
index 75bab8dc84..b5401d788d 100644
1be5c7
--- a/tests/qemu-iotests/108.out
1be5c7
+++ b/tests/qemu-iotests/108.out
1be5c7
@@ -105,6 +105,87 @@ The following inconsistencies were found and repaired:
1be5c7
     0 leaked clusters
1be5c7
     1 corruptions
1be5c7
 
1be5c7
+Double checking the fixed image now...
1be5c7
+No errors were found on the image.
1be5c7
+
1be5c7
+=== Check rebuilt reftable location ===
1be5c7
+
1be5c7
+--- Does the image size increase? ---
1be5c7
+
1be5c7
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
1be5c7
+wrote 65536/65536 bytes at offset 0
1be5c7
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1be5c7
+
1be5c7
+ERROR cluster 0 refcount=0 reference=1
1be5c7
+Rebuilding refcount structure
1be5c7
+The following inconsistencies were found and repaired:
1be5c7
+
1be5c7
+    0 leaked clusters
1be5c7
+    1 corruptions
1be5c7
+
1be5c7
+Double checking the fixed image now...
1be5c7
+No errors were found on the image.
1be5c7
+
1be5c7
+OK: Image size did not change
1be5c7
+
1be5c7
+--- Will the reftable occupy a hole specifically left for it?  ---
1be5c7
+
1be5c7
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=9437184
1be5c7
+wrote 8388608/8388608 bytes at offset 0
1be5c7
+8 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1be5c7
+discard 512/512 bytes at offset 0
1be5c7
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1be5c7
+discard 1024/1024 bytes at offset 4096
1be5c7
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1be5c7
+
1be5c7
+ERROR cluster 0 refcount=0 reference=1
1be5c7
+Rebuilding refcount structure
1be5c7
+The following inconsistencies were found and repaired:
1be5c7
+
1be5c7
+    0 leaked clusters
1be5c7
+    1 corruptions
1be5c7
+
1be5c7
+Double checking the fixed image now...
1be5c7
+No errors were found on the image.
1be5c7
+
1be5c7
+OK: Reftable is where we expect it
1be5c7
+
1be5c7
+--- Rebuilding refcount structures on block devices ---
1be5c7
+
1be5c7
+{ "execute": "qmp_capabilities" }
1be5c7
+{"return": {}}
1be5c7
+{ "execute": "blockdev-create",
1be5c7
+       "arguments": {
1be5c7
+           "job-id": "create",
1be5c7
+           "options": {
1be5c7
+               "driver": "IMGFMT",
1be5c7
+               "file": "file",
1be5c7
+               "size": 67108864,
1be5c7
+               "cluster-size": 512
1be5c7
+           } } }
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "create"}}
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "create"}}
1be5c7
+{"return": {}}
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "create"}}
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "create"}}
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "create"}}
1be5c7
+{ "execute": "job-dismiss", "arguments": { "id": "create" } }
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}}
1be5c7
+{"return": {}}
1be5c7
+{ "execute": "quit" }
1be5c7
+{"return": {}}
1be5c7
+{"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
1be5c7
+
1be5c7
+wrote 65536/65536 bytes at offset 0
1be5c7
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1be5c7
+
1be5c7
+ERROR cluster 0 refcount=0 reference=1
1be5c7
+Rebuilding refcount structure
1be5c7
+The following inconsistencies were found and repaired:
1be5c7
+
1be5c7
+    0 leaked clusters
1be5c7
+    1 corruptions
1be5c7
+
1be5c7
 Double checking the fixed image now...
1be5c7
 No errors were found on the image.
1be5c7
 *** done
1be5c7
-- 
1be5c7
2.27.0
1be5c7