#!/bin/bash
# FS QA Test No. xfs/010
#
# Test xfs_repair of the free inode btree (finobt). Make a couple targeted
# corruptions and verify that xfs_repair detects and repairs the filesystem to
# a consistent state.
#
#-----------------------------------------------------------------------
# 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!

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

_cleanup()
{
	cd /
	_scratch_unmount 2>/dev/null
	rm -f $tmp.*
}
trap "_cleanup; exit \$status" 0 1 2 3 15

_sparse_inode_populate()
{
	dir=$1
	count=$2

	for i in $(seq 0 $count)
	do
		touch $dir/$i
	done

	# Inode chunks are allocated 64 inodes at a time. If we remove 1 out of
	# every 32 we allocated above, we'll end up leaving an inode or two free
	# in each chunk. This ensures that most records are inserted into the
	# finobt.
	for i in $(seq 0 32 $count)
	do
		rm -f $dir/$i
	done
}

_filter_dbval()
{
	awk '{ print $3 }'
}

_corrupt_finobt_records()
{
	dev=$1

	# determine the root block of the finobt
	free_root=`$XFS_DB_PROG -c "agi 0" -c "p free_root" $dev |
			_filter_dbval`

	# Corrupt a freecount value. This should never exceed 64.
	$XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
		-c "write recs[1].freecount 70" $dev

	# Create a corrupted non-free record, which should never appear in the
	# finobt.
	$XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
		 -c "write recs[2].freecount 0" $dev
	$XFS_DB_PROG -x -c "fsb $free_root" -c "type inobt" \
		-c "write recs[2].free 0" $dev
}

_corrupt_finobt_root()
{
	dev=$1

	# nuke the agi finobt root fields
	$XFS_DB_PROG -x				\
		-c "agi 0"			\
		-c "write -c free_root 0"	\
		-c "write -c free_level 0"	\
		$dev | grep -v "Allowing write of corrupted"
}

# real QA test starts here
_supported_fs xfs
_supported_os Linux

_require_scratch
_require_xfs_mkfs_finobt
_require_xfs_finobt

rm -f $seqres.full

_scratch_mkfs_xfs "-m crc=1,finobt=1 -d agcount=2" | _filter_mkfs 2>$seqres.full

# sparsely populate the fs such that we create records with free inodes
_scratch_mount
_sparse_inode_populate $SCRATCH_MNT 999
_scratch_unmount

# corrupt some finobt records
_corrupt_finobt_records $SCRATCH_DEV

# repair should detect the inconsistencies
_scratch_xfs_repair 2>&1 | _filter_repair
_check_scratch_fs

# nuke the finobt root, repair will have to regenerate from the inobt
_corrupt_finobt_root $SCRATCH_DEV

_scratch_xfs_repair 2>&1 | _filter_repair

status=0
exit
