#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright 2011 (C) Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
#
# FS QA Test No. 260
#
# Purpose of this test is to check FITRIM argument handling to make sure
# that the argument processing is right and that it does not overflow.
#
. ./common/preamble
_begin_fstest auto quick trim

status=0
chpid=0
mypid=$$

# Import common functions.
. ./common/filter

# real QA test starts here
_supported_fs generic
_require_math

_require_scratch
_scratch_mkfs >/dev/null 2>&1
_scratch_mount

_require_batched_discard $SCRATCH_MNT

fssize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV")

beyond_eofs=$(_math "$fssize*2048")
max_64bit=$(_math "2^64 - 1")
[ $FSTYP == "btrfs" ] && beyond_eofs=$max_64bit

# All these tests should return EINVAL
# since the start is beyond the end of
# the file system

echo "[+] Start beyond the end of fs (should fail)"
out=$($FSTRIM_PROG -o $beyond_eofs $SCRATCH_MNT 2>&1)
[ $? -eq 0 ] && status=1
echo $out | _filter_scratch

echo "[+] Start beyond the end of fs with len set (should fail)"
out=$($FSTRIM_PROG -o $beyond_eofs -l1M $SCRATCH_MNT 2>&1)
[ $? -eq 0 ] && status=1
echo $out | _filter_scratch

echo "[+] Start = 2^64-1 (should fail)"
out=$($FSTRIM_PROG -o $max_64bit $SCRATCH_MNT 2>&1)
[ $? -eq 0 ] && status=1
echo $out | _filter_scratch

echo "[+] Start = 2^64-1 and len is set (should fail)"
out=$($FSTRIM_PROG -o $max_64bit -l1M $SCRATCH_MNT 2>&1)
[ $? -eq 0 ] && status=1
echo $out | _filter_scratch

_scratch_unmount
_scratch_mkfs >/dev/null 2>&1
_scratch_mount

# All these tests should succeed
# since the length should be truncated

echo "[+] Default length (should succeed)"
$FSTRIM_PROG $SCRATCH_MNT
[ $? -ne 0 ] && status=1
echo "[+] Default length with start set (should succeed)"
$FSTRIM_PROG -o10M $SCRATCH_MNT
[ $? -ne 0 ] && status=1
echo "[+] Length beyond the end of fs (should succeed)"
$FSTRIM_PROG -l $beyond_eofs $SCRATCH_MNT
[ $? -ne 0 ] && status=1
echo "[+] Length beyond the end of fs with start set (should succeed)"
$FSTRIM_PROG -o10M -l $beyond_eofs $SCRATCH_MNT
[ $? -ne 0 ] && status=1

_scratch_unmount
_scratch_mkfs >/dev/null 2>&1
_scratch_mount

# This is a bit fuzzy, but since the file system is fresh
# there should be at least (fssize/2) free space to trim.
# This is supposed to catch wrong FITRIM argument handling
bytes=$($FSTRIM_PROG -v -o10M $SCRATCH_MNT | _filter_fstrim)

if [ $bytes -gt $(_math "$fssize*1024") ]; then
	status=1
	echo "After the full fs discard $bytes bytes were discarded"\
	     "however the file system is $(_math "$fssize*1024") bytes long."
fi

# Btrfs is special and this test does not apply to it
# It is because btrfs does not have not-yet-used parts of the device
# mapped and since we got here right after the mkfs, there is not
# enough free extents in the root tree.
# F2fs is also special. F2fs can tag a special flag TRIMMED_FLAG to
# indicate the whole filesystem is trimmed, so after mkfs/fstrim(),
# following fstrim() won't trim any block.
if [ $bytes -le $(_math "$fssize*512") ] && [ $FSTYP != "btrfs" ] && [ $FSTYP != "f2fs" ]; then
	status=1
	echo "After the full fs discard $bytes bytes were discarded"\
	     "however the file system is $(_math "$fssize*1024") bytes long."
fi

# Now to catch overflows due to fsblk->allocation group number conversion
# This is different for every file system and it also apply just to some of
# them. In order to add check specific for file system you're interested in
# compute the arguments as you need and make the file system with proper
# alignment

# (2^32-1) + 2 (this is set to overflow 32bit variable by 2)
base=$(_math "2^32+1")

case $FSTYP in
	ext[34])
		agsize=32768
		bsize=4096
		start=$(_math "$base*$agsize*$bsize")
		len=$start
		export MKFS_OPTIONS="-F -b $bsize -g $agsize"
		if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
		    MKFS_OPTIONS+=" -O encrypt"
		fi
		;;
	xfs)
		agsize=65538
		bsize=4096
		start=$(_math "$base*$agsize*$bsize")
		len=$start
		export MKFS_OPTIONS="-f -d agsize=$(_math "$agsize*$bsize") -b size=$bsize"
		;;
	btrfs)
		# Btrfs doesn't care about any of this, just test max_64bit
		# since it'll fail
		start=$max_64bit
		len=$(_math "$start / 2")
		;;
	*)
		# (2^32-1) * 4096 * 65536 == 32bit max size * block size * ag size
		start=$(_math "(2^32 - 1) * 4096 * 65536")
		len=$start
		;;
esac

_scratch_unmount
_scratch_mkfs >/dev/null 2>&1
_scratch_mount
# It should fail since $start is beyond the end of file system
$FSTRIM_PROG -o$start -l10M $SCRATCH_MNT &> /dev/null
if [ $? -eq 0 ]; then
	status=1
	echo "It seems that fs logic handling start"\
	     "argument overflows"
fi

_scratch_unmount
_scratch_mkfs >/dev/null 2>&1
_scratch_mount

# len should be big enough to cover the whole file system, so if the
# number of discarded bytes is smaller than file system size/2 then it
# most likely overflowed
# Btrfs is special and this test does not apply to it
# It is because btrfs does not have not-yet-used parts of the device
# mapped and since we got here right after the mkfs, there is not
# enough free extents in the root tree.
# F2fs is also special. F2fs can tag a special flag TRIMMED_FLAG to
# indicate the whole filesystem is trimmed, so after mkfs/fstrim(),
# following fstrim() won't trim any block.
bytes=$($FSTRIM_PROG -v -l$len $SCRATCH_MNT | _filter_fstrim)
if [ $bytes -le $(_math "$fssize*512") ] && [ $FSTYP != "btrfs" ] && [ $FSTYP != "f2fs" ]; then
	status=1
	echo "It seems that fs logic handling len argument overflows"
fi

echo "Test done"
exit
