Files
apfstests/tests/xfs/444
T
Darrick J. Wong 7e98d41a6e fstests: move test group info to test files
Refactor every test in the entire test suite to use the new boilerplate
functions.  This also migrates all the test group information into the
test files.  This patch has been autogenerated via the command:

./tools/convert-group btrfs ceph cifs ext4 f2fs generic nfs ocfs2 overlay perf shared udf xfs

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
2021-06-27 22:50:02 +08:00

236 lines
7.2 KiB
Bash
Executable File

#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2018 Oracle, Inc.
#
# FS QA Test No. 444
#
# Make sure XFS can fix a v5 AGFL that wraps over the last block.
# Refer to commit 96f859d52bcb ("libxfs: pack the agfl header structure so
# XFS_AGFL_SIZE is correct") for details on the original on-disk format error
# and the patch "xfs: detect agfl count corruption and reset agfl") for details
# about the fix.
#
. ./common/preamble
_begin_fstest auto quick
_register_cleanup "_cleanup; rm -f $tmp.*"
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
_require_check_dmesg
_require_scratch
_require_test_program "punch-alternating"
_require_xfs_io_command "falloc"
_require_xfs_db_write_array
# This is only a v5 filesystem problem
_require_scratch_xfs_crc
# Disable the scratch rt device to avoid test failures relating to the rt
# bitmap consuming free space in our small data device and throwing off the
# filestreams allocator.
unset SCRATCH_RTDEV
mount_loop() {
if ! _try_scratch_mount >> $seqres.full 2>&1; then
echo "scratch mount failed" >> $seqres.full
return
fi
# Trigger agfl fixing by fragmenting free space enough to cause
# a bnobt split
blksz=$(_get_file_block_size ${SCRATCH_MNT})
bno_maxrecs=$(( blksz / 8 ))
filesz=$((bno_maxrecs * 3 * blksz))
rm -rf $SCRATCH_MNT/a
$XFS_IO_PROG -f -c "falloc 0 $filesz" $SCRATCH_MNT/a >> $seqres.full 2>&1
test -e $SCRATCH_MNT/a && $here/src/punch-alternating $SCRATCH_MNT/a
rm -rf $SCRATCH_MNT/a
_scratch_unmount 2>&1 | _filter_scratch
}
dump_ag0() {
_scratch_xfs_db -c 'sb 0' -c 'p' -c 'agf 0' -c 'p' -c 'agfl 0' -c 'p'
}
runtest() {
cmd="$1"
# Format filesystem
echo "TEST $cmd" | tee /dev/ttyprintk
echo "TEST $cmd" >> $seqres.full
_scratch_mkfs >> $seqres.full
# Record what was here before
echo "FS BEFORE" >> $seqres.full
dump_ag0 > $tmp.before
cat $tmp.before >> $seqres.full
sectsize=$(_scratch_xfs_get_metadata_field "sectsize" "sb 0")
flfirst=$(_scratch_xfs_get_metadata_field "flfirst" "agf 0")
fllast=$(_scratch_xfs_get_metadata_field "fllast" "agf 0")
flcount=$(_scratch_xfs_get_metadata_field "flcount" "agf 0")
# Due to a padding bug in the original v5 struct xfs_agfl,
# XFS_AGFL_SIZE could be 36 on 32-bit or 40 on 64-bit. On a system
# with 512b sectors, this means that the AGFL length could be
# ((512 - 36) / 4) = 119 entries on 32-bit or ((512 - 40) / 4) = 118
# entries on 64-bit.
#
# We now have code to figure out if the AGFL list wraps incorrectly
# according to the kernel's agfl size and fix it by resetting the agfl
# to zero length. Mutate ag 0's agfl to be in various configurations
# and see if we can trigger the reset.
#
# Don't hardcode the numbers, calculate them.
# Have to have at least three agfl items to test full wrap
test "$flcount" -ge 3 || _notrun "insufficient agfl flcount"
# mkfs should be able to make us a nice neat flfirst < fllast setup
test "$flfirst" -lt "$fllast" || _notrun "fresh agfl already wrapped?"
bad_agfl_size=$(( (sectsize - 40) / 4 ))
good_agfl_size=$(( (sectsize - 36) / 4 ))
agfl_size=
case "$1" in
"fix_end") # fllast points to the end w/ 40-byte padding
new_flfirst=$(( bad_agfl_size - flcount ))
agfl_size=$bad_agfl_size;;
"fix_start") # flfirst points to the end w/ 40-byte padding
new_flfirst=$(( bad_agfl_size - 1))
agfl_size=$bad_agfl_size;;
"fix_wrap") # list wraps around end w/ 40-byte padding
new_flfirst=$(( bad_agfl_size - (flcount / 2) ))
agfl_size=$bad_agfl_size;;
"start_zero") # flfirst points to the start
new_flfirst=0
agfl_size=$good_agfl_size;;
"good_end") # fllast points to the end w/ 36-byte padding
new_flfirst=$(( good_agfl_size - flcount ))
agfl_size=$good_agfl_size;;
"good_start") # flfirst points to the end w/ 36-byte padding
new_flfirst=$(( good_agfl_size - 1 ))
agfl_size=$good_agfl_size;;
"good_wrap") # list wraps around end w/ 36-byte padding
new_flfirst=$(( good_agfl_size - (flcount / 2) ))
agfl_size=$good_agfl_size;;
"bad_start") # flfirst points off the end
new_flfirst=$good_agfl_size
agfl_size=$good_agfl_size;;
"no_move") # whatever mkfs formats (flfirst points to start)
new_flfirst=$flfirst
agfl_size=$good_agfl_size;;
"simple_move") # move list arbitrarily
new_flfirst=$((fllast + 1))
agfl_size=$good_agfl_size;;
*)
_fail "Internal test error";;
esac
new_fllast=$(( (new_flfirst + flcount - 1) % agfl_size ))
# Log what we're doing...
cat >> $seqres.full << ENDL
sector size: $sectsize
bad_agfl_size: $bad_agfl_size [0 - $((bad_agfl_size - 1))]
good_agfl_size: $good_agfl_size [0 - $((good_agfl_size - 1))]
agfl_size: $agfl_size
flfirst: $flfirst
fllast: $fllast
flcount: $flcount
new_flfirst: $new_flfirst
new_fllast: $new_fllast
ENDL
# Remap the agfl blocks
echo "$((good_agfl_size - 1)) 0xffffffff" > $tmp.remap
seq "$flfirst" "$fllast" | while read f; do
list_pos=$((f - flfirst))
dest_pos=$(( (new_flfirst + list_pos) % agfl_size ))
bno=$(_scratch_xfs_get_metadata_field "bno[$f]" "agfl 0")
echo "$dest_pos $bno" >> $tmp.remap
done
cat $tmp.remap | while read dest_pos bno junk; do
_scratch_xfs_set_metadata_field "bno[$dest_pos]" "$bno" \
"agfl 0" >> $seqres.full
done
# Set new flfirst/fllast
_scratch_xfs_set_metadata_field "fllast" "$new_fllast" \
"agf 0" >> $seqres.full
_scratch_xfs_set_metadata_field "flfirst" "$new_flfirst" \
"agf 0" >> $seqres.full
echo "FS AFTER" >> $seqres.full
dump_ag0 > $tmp.corrupt 2> /dev/null
diff -u $tmp.before $tmp.corrupt >> $seqres.full
# Mount and see what happens
mount_loop
# Did we end up with a non-wrapped list?
flfirst=$(_scratch_xfs_get_metadata_field "flfirst" "agf 0" 2>/dev/null)
fllast=$(_scratch_xfs_get_metadata_field "fllast" "agf 0" 2>/dev/null)
echo "flfirst=${flfirst} fllast=${fllast}" >> $seqres.full
if [ "${flfirst}" -ge "$((good_agfl_size - 1))" ]; then
echo "ASSERT flfirst < good_agfl_size - 1" | tee -a $seqres.full
fi
if [ "${fllast}" -ge "$((good_agfl_size - 1))" ]; then
echo "ASSERT fllast < good_agfl_size - 1" | tee -a $seqres.full
fi
if [ "${flfirst}" -ge "${fllast}" ]; then
echo "ASSERT flfirst < fllast" | tee -a $seqres.full
fi
echo "FS MOUNTLOOP" >> $seqres.full
dump_ag0 > $tmp.mountloop 2> /dev/null
diff -u $tmp.corrupt $tmp.mountloop >> $seqres.full
# Let's see what repair thinks
echo "REPAIR" >> $seqres.full
_scratch_xfs_repair >> $seqres.full 2>&1
echo "FS REPAIR" >> $seqres.full
dump_ag0 > $tmp.repair 2> /dev/null
diff -u $tmp.mountloop $tmp.repair >> $seqres.full
# Exercise the filesystem again to make sure there aren't any lasting
# ill effects from either the agfl reset or the recommended subsequent
# repair run.
mount_loop
echo "FS REMOUNT" >> $seqres.full
dump_ag0 > $tmp.remount 2> /dev/null
diff -u $tmp.repair $tmp.remount >> $seqres.full
}
runtest fix_end
runtest fix_start
runtest fix_wrap
runtest start_zero
runtest good_end
runtest good_start
runtest good_wrap
runtest bad_start
runtest no_move
runtest simple_move
# Did we get the kernel warning too?
warn_str='WARNING: Reset corrupted AGFL'
_check_dmesg_for "${warn_str}" || echo "Missing dmesg string \"${warn_str}\"."
# Now run the regular dmesg check, filtering out the agfl warning
filter_agfl_reset_printk() {
grep -v "${warn_str}"
}
_check_dmesg filter_agfl_reset_printk
status=0
exit