#! /bin/bash
# FS QA Test 410
#
# Test mount shared subtrees, verify the state transition when use:
#   --make-shared
#   --make-slave
#   --make-private
#   --make-unbindable
#
# ------------------------------------------------------------------------
# |             |make-shared |  make-slave  | make-private |make-unbindab|
# --------------|------------|--------------|--------------|-------------|
# |shared       |shared      |*slave/private|   private    | unbindable  |
# |             |            |              |              |             |
# |-------------|------------|--------------|--------------|-------------|
# |slave        |shared      |    **slave   |    private   | unbindable  |
# |             |and slave   |              |              |             |
# |-------------|------------|--------------|--------------|-------------|
# |shared       |shared      |    slave     |    private   | unbindable  |
# |and slave    |and slave   |              |              |             |
# |-------------|------------|--------------|--------------|-------------|
# |private      |shared      |  **private   |    private   | unbindable  |
# |-------------|------------|--------------|--------------|-------------|
# |unbindable   |shared      |**unbindable  |    private   | unbindable  |
# ------------------------------------------------------------------------
#
#-----------------------------------------------------------------------
# Copyright (c) 2016 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 /
	rm -f $tmp.*
	_clear_mount_stack
	# make sure there's no bug cause dentry isn't be freed
	rm -rf $MNTHEAD
}

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

# remove previous $seqres.full before test
rm -f $seqres.full

# real QA test starts here
_supported_fs generic
_supported_os Linux
_require_test
_require_scratch
_require_block_device $SCRATCH_DEV

fs_stress()
{
	local target=$1

	$FSSTRESS_PROG -z -n 50 -p 3 \
		       -f creat=5 \
		       -f mkdir=5 \
		       -f link=2 \
		       -f rename=1 \
		       -f rmdir=2 \
		       -f unlink=1 \
		       -f symlink=1 \
		       -f write=1 \
		       -f read=1 \
		       -f chown=1 \
		       -f getdents=1 \
		       -f fiemap=1 \
		       -d $target >/dev/null
	sync
}

# prepare some mountpoint dir
MNTHEAD=$TEST_DIR/$seq
rm -rf $MNTHEAD
mkdir $MNTHEAD 2>>$seqres.full
mpA=$MNTHEAD/"$$"_mpA
mpB=$MNTHEAD/"$$"_mpB
mpC=$MNTHEAD/"$$"_mpC

find_mnt()
{
	echo "------"
	findmnt -n -o TARGET,SOURCE $SCRATCH_DEV | \
		sed -e "s;$mpA;mpA;g" \
		    -e "s;$mpB;mpB;g" \
		    -e "s;$mpC;mpC;g" | \
		_filter_spaces | _filter_scratch | \
		_filter_test_dir | sort
	echo "======"
}

start_test()
{
	local type=$1

	_scratch_mkfs >$seqres.full 2>&1
	_get_mount $SCRATCH_DEV $MNTHEAD
	$MOUNT_PROG --make-"${type}" $MNTHEAD
	mkdir $mpA $mpB $mpC
}

end_test()
{
	_clear_mount_stack
	rm -rf $mpA $mpB $mpC
}

run()
{
	# command include make-shared/slave/private/unbindable
	local cmd=$1
	# orginal status include shared/slave/shared&slave/private/unbindable
	local orgs="$2"

	# bind anything on un-shared dest will keep the source type
	# So use un-shared dest at here
	start_test private

	echo "make-$cmd a $orgs mount"
	_get_mount $SCRATCH_DEV $mpA
	mkdir -p $mpA/dir 2>/dev/null
	$MOUNT_PROG --make-shared $mpA

	# prepare the original status on mpB
	_get_mount --bind $mpA $mpB
	# shared&slave status need to do make-slave then make-shared
	# two operations.
	for t in $orgs; do
		$MOUNT_PROG --make-"$t" $mpB
	done

	# "before" for prepare and check original status
	# "after" for check the impact of make-$cmd command
	for i in before after; do
		echo "$i make-$cmd run on $orgs"
		# maybe unbindable at here
		_get_mount --bind $mpB $mpC 2>/dev/null
		if [ $? -ne 0 ];then
			find_mnt
		else
			for m in $mpA $mpB $mpC; do
				_get_mount $SCRATCH_DEV $m/dir
				fs_stress $m/dir
				find_mnt
				_put_mount
			done
			_put_mount # umount C
		fi
		if [ "$i" = "before" ];then
			$MOUNT_PROG --make-"${cmd}" $mpB
		fi
	done

	end_test
}

do_test()
{
    #   operation  original_status
    run shared     shared
    run shared     slave
    run shared     "slave shared"
    run shared     private
    run shared     unbindable

    run slave      shared
    run slave      slave
    run slave      "slave shared"
    run slave      private
    run slave      unbindable

    run private    shared
    run private    slave
    run private    "slave shared"
    run private    private
    run private    unbindable

    run unbindable shared
    run unbindable slave
    run unbindable "slave shared"
    run unbindable private
    run unbindable unbindable
}

do_test

# success, all done
status=0
exit
