mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
7e98d41a6e
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>
236 lines
7.2 KiB
Bash
Executable File
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
|