##/bin/bash
# Routines for injecting errors into filesystems
#-----------------------------------------------------------------------
#  Copyright (c) 2016 Oracle.  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; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will 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 to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
#  USA
#
#  Contact information: Oracle Corporation, 500 Oracle Parkway,
#  Redwood Shores, CA 94065, USA, or: http://www.oracle.com/
#-----------------------------------------------------------------------
. ./common/log

# Tests whether $FSTYP is one of the filesystems that supports error injection
_require_error_injection()
{
	case "$FSTYP" in
	"xfs")
		grep -q 'debug 1' /proc/fs/xfs/stat || \
			_notrun "XFS error injection requires CONFIG_XFS_DEBUG"
		;;
	*)
		_notrun "Error injection not supported on filesystem type: $FSTYP"
	esac
}

# Find the errortag injection knob in sysfs for a given xfs mount's
# block device.
_find_xfs_mountdev_errortag_knob()
{
	dev="$1"
	knob="$2"
	shortdev="$(_short_dev "${dev}")"
	tagfile="/sys/fs/xfs/${shortdev}/errortag/${knob}"

	# Some of the new sysfs errortag knobs were previously available via
	# another sysfs path.
	case "${knob}" in
	"log_bad_crc")
		if [ ! -w "${tagfile}" ]; then
			tagfile="/sys/fs/xfs/${shortdev}/log/log_badcrc_factor"
		fi
		;;
	"drop_writes")
		if [ ! -w "${tagfile}" ]; then
			tagfile="/sys/fs/xfs/${shortdev}/drop_writes"
		fi
		if [ ! -w "${tagfile}" ]; then
			tagfile="/sys/fs/xfs/${shortdev}/fail_writes"
		fi
		;;
	*)
		;;
	esac

	echo "${tagfile}"
}

# Requires that xfs_io inject command knows about this error type
_require_xfs_io_error_injection()
{
	type="$1"
	_require_error_injection

	# Can we find the error injection knobs via the new errortag
	# configuration mechanism?
	knob="$(_find_xfs_mountdev_errortag_knob "${TEST_DEV}" "${type}")"
	test -w "${knob}" && return

	# NOTE: We can't actually test error injection here because xfs
	# hasn't always range checked the argument to xfs_errortag_add.
	# We also don't want to trip an error before we're ready to deal
	# with it.

	$XFS_IO_PROG -x -c 'inject' $TEST_DIR | grep -q "$type" || \
		_notrun "XFS error injection $type unknown."
}

# Inject an error into the test fs
_test_inject_error()
{
	type="$1"
	value="$2"

	knob="$(_find_xfs_mountdev_errortag_knob "${TEST_DEV}" "${type}")"
	if [ -w "${knob}" ]; then
		test -z "${value}" && value="default"
		echo -n "${value}" > "${knob}"
	elif [ -z "${value}" ] || [ "${value}" = "default" ]; then
		$XFS_IO_PROG -x -c "inject $type" $TEST_DIR
	else
		_fail "Cannot inject error ${type} value ${value}."
	fi
}

# Inject an error into the scratch fs
_scratch_inject_error()
{
	type="$1"
	value="$2"

	knob="$(_find_xfs_mountdev_errortag_knob "${SCRATCH_DEV}" "${type}")"
	if [ -w "${knob}" ]; then
		test -z "${value}" && value="default"
		echo -n "${value}" > "${knob}"
	elif [ -z "${value}" ] || [ "${value}" = "default" ]; then
		$XFS_IO_PROG -x -c "inject $type" $SCRATCH_MNT
	else
		_fail "Cannot inject error ${type} value ${value}."
	fi
}

# Unmount and remount the scratch device, dumping the log
_scratch_inject_logprint()
{
	local opts="$1"

	if test -n "$opts"; then
		opts="-o $opts"
	fi
	_scratch_unmount
	_scratch_dump_log
	_scratch_mount "$opts"
}

# Unmount and remount the test device, dumping the log
_test_inject_logprint()
{
	local opts="$1"

	if test -n "$opts"; then
		opts="-o $opts"
	fi
	_test_unmount
	_test_dump_log
	_test_mount "$opts"
}
