#! /bin/bash
# FS QA Test No. 032
#
# This test implements a data corruption scenario on XFS filesystems with
# sub-page sized blocks and unwritten extents. Inode lock contention during
# writeback of pages to unwritten extents leads to failure to convert those
# extents on I/O completion. This causes data corruption as unwritten extents
# are always read back as zeroes.
#
#-----------------------------------------------------------------------
# Copyright (c) 2014 Red Hat, Inc.  All Rights Reserved.
#
# 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.
#
# This program is distributed in the hope that it would 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, write the Free Software Foundation,
# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#-----------------------------------------------------------------------
#

seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"

here=`pwd`
tmp=/tmp/$$
status=1	# failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15

_cleanup()
{
	cd /
	kill -9 $syncpid > /dev/null 2>&1
	wait
	rm -f $tmp.*
}

# get standard environment, filters and checks
. ./common/rc
. ./common/punch

# real QA test starts here
rm -f $seqres.full

_syncloop()
{
	while [ true ]; do
		sync
	done
}

# Modify as appropriate.
_supported_fs generic
_supported_os Linux
_require_scratch
_require_xfs_io_command "falloc"
_require_xfs_io_command "fiemap"

_scratch_mkfs >/dev/null 2>&1
_scratch_mount

# run background sync thread
_syncloop &
syncpid=$!

for iters in $(seq 1 100)
do
	rm -f $SCRATCH_MNT/file

	# create a delalloc block in each page of the first 64k of the file
	for pgoff in $(seq 0 0x1000 0xf000); do
		offset=$((pgoff + 0xc00))
		$XFS_IO_PROG -f \
			-c "pwrite $offset 0x1" \
			$SCRATCH_MNT/file >> $seqres.full 2>&1
	done

	# preallocate the first 64k and overwite, writing past 64k to contend
	# with writeback
	$XFS_IO_PROG \
		-c "falloc 0 0x10000"	\
		-c "pwrite 0 0x100000"	\
		-c "fsync"		\
		$SCRATCH_MNT/file >> $seqres.full 2>&1

	# Check for unwritten extents. We should have none since we wrote over
	# the entire preallocated region and ran fsync.
	$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/file | \
		tee -a $seqres.full | \
		_filter_fiemap | grep unwritten
	[ $? == 0 ] && _fail "Unwritten extents found!"
done

echo $iters iterations

kill $syncpid
wait

# clear page cache and dump the file
_scratch_cycle_mount
hexdump $SCRATCH_MNT/file

status=0
exit
