#!/bin/bash

shopt -s extglob

eR='\e[31m';eG='\e[32m';eY='\e[33m';eB='\e[34m';eN='\e[0m'

function eecho () {
    C=$1; shift
    echo -e "$C$*$eN"
}

function techo () {
    C=$1; shift
    echo -en "$C$*$eN"
}

function ececho () {
    rc=$1; shift
    id=$1; shift
    if [ $rc -eq 0 ]; then
	[ $terse -ne 0 ] && techo $eG "$id. " || \
        eecho $eG "$id# succeeded."
    else
	[ $terse -ne 0 ] && techo $eR "$id* " || \
        eecho $eR "$id# failed."
    fi 
    return $rc
}

function eexec () {
    local id=`printf "[%3.3s]" $1`; shift
    local eret=$1; shift
    
    echo -e "\t$*" >$outdev
    if [ $terse -gt 0 ]; then
	eval $* 1>/dev/null 2>&1 3>&1
    else
    	eval $* 3>&2 1>$outdev 2>$errdev
    fi
    local ret=$?
    [ -n "$eret" -a "$eret" -ne 0 ] \
        && ret=$(( ret - eret ))
    ececho $ret $id
    return $ret
}


function go_xid () {
    local xid=$1; shift

    if [ "$version" == "24" -a $xid -gt 0 ]; then
	echo -e "\tchcontext --ctx $xid $*"
	chcontext --ctx $xid $*
    elif [ $xid -gt 1 ]; then
	echo -e "\tvcontext --create --xid $xid -- $*"
	vcontext --create --xid $xid -- $*
    elif [ $xid -eq 1 ]; then
	echo -e "\tvcontext --migrate --xid 1 -- $*"
	vcontext --migrate --xid 1 -- $*
    else
	echo -e "\t$*"
	$*
    fi
}

function mnt_test () {
    local rc;

    mount -o $3 $1 $2 >/dev/null 2>&1
    rc=$?
    [ $rc -eq 0 ] && umount -f $2 >/dev/null 2>&1
    return $rc  
}

function do_xid_touch () {
    local path="$1"; shift
    local pos=1
    local ret=0

    while [ $# -gt 0 ]; do
	local file="$path/file_$pos"
	echo -e "\ttouch $file: $1"
	go_xid $1 "touch $file" 
	local rc=$?
	[ $rc -eq 0 ] || { ret=1;
	    eecho $eR "\ttouch $file: $1 [$rc]" 1>&3; }
	shift
	pos=$[pos+1];
    done
    return $ret
}

function do_xid_verify () {
    local path="$1"; shift
    local pos=1 ret=0

    while [ $# -gt 0 ]; do
	local file="$path/file_$pos"
	val=`lsxid -d $file | awk '{print $1}'`
	echo -e "\tverify $file: $1 = $val"
	[ "$val" -eq "$1" ] || { ret=1;
	    eecho $eR "\tverify $file: $1 = $val" 1>&3; }
	shift
	pos=$[pos+1];
    done
    return $ret
}

function do_xid_change () {
    local path="$1"; shift
    local pos=1 ret=0

    while [ $# -gt 0 ]; do
	local file="$path/file_$pos"
	echo -e "\tchange $file: $1"
	chxid -c $1 $file
	local rc=$?
	[ $rc -eq 0 ] || { ret=1; 
	    eecho $eR "\tchange $file: $1 [$rc]" 1>&3; }
	shift
	pos=$[pos+1];
    done
    return $ret
}

function do_xid_read () {
    local path="$1"; shift
    local pos=1 ret=0

    while [ $# -gt 1 ]; do
	local file="$path/file_$pos"
        local x=0 xx=""

	for x in 0 1 2 $1; do
	    go_xid $x cat "$file" >/dev/null
   	    local rc=$?
	    [ $rc -eq 0 ] && xx="$xx." || xx="$xx^"
	done
	[ "$xx" == "$2" ] || { ret=1;
	    eecho $eR "\tread $file: $1 [$xx:$2]" 1>&3; }
	
	shift; shift
	pos=$[pos+1];
    done
    return $ret
}

function do_xid_write () {
    local path="$1"; shift
    local pos=1 ret=0

    while [ $# -gt 1 ]; do
	local file="$path/file_$pos"
        local x=0 xx=""

	for x in 0 1 2 $1; do
	    echo "test-$pos" | go_xid $x tee "$file" >/dev/null
   	    local rc=$?
	    [ $rc -eq 0 ] && xx="$xx." || xx="$xx^"
	done
	[ "$xx" == "$2" ] || { ret=1;
	    eecho $eR "\twrite $file: [$xx:$2] $1" 1>&3; }
	
	shift; shift
	pos=$[pos+1];
    done
    return $ret
}


function do_xattr_barrier () {
    local path="$1"; shift
    local bdir="$1"; shift
    local ret=0

    local x=0 xx=""
    for x in 0 1 2; do
        go_xid $x chmod +x "$bdir"
   	local rc=$?
	chmod -x "$bdir"
	[ $rc -eq 0 ] && xx="$xx." || xx="$xx^"
    done
    [ "$xx" == "$1" ] || { ret=1;
        eecho $eR "\tbarrier $bdir: [$xx:$1]" 1>&3; }
    return $ret
}

function do_xattr_iunlink () {
    local path="$1"; shift
    local file="$1"; shift
    local ret=0

    local x=0 xx=""
    for x in 0 1 2; do
        go_xid $x chmod +x "$file"
   	local rc=$?
	chmod -x "$file"
	[ $rc -eq 0 ] && xx="$xx." || xx="$xx^"
    done
    [ "$xx" == "$1" ] || { ret=1;
        eecho $eR "\tiunlink chmod $file: [$xx:$1]" 1>&3; }
    return $ret
}

function do_xattr_verify () {
    local path="$1"; shift
    local vcmd="$1"; shift
    local pos=1 ret=0

    while [ $# -gt 0 ]; do
	local file="$1"; shift
	val=`$vcmd -d $file | awk '{print $1}'`
	echo -e "\tverify $file: $1 = $val"
	case $val in
	   $1) ;;
	   *)	ret=1
	        eecho $eR "\tverify $file: $1 ~ $val" 1>&3
		;;
	esac
	shift
    done
    return $ret
}

function do_sync () {
    sync
    sleep 6
}

function do_test () {
    local mopt=$2
    local xopt="$mopt,$tagxid"
    local X="-"
	
    do_sync
    [ $test_xid -gt 0 ] && {

	[ $terse -ne 0 ] && echo "($1 format)"
    	eecho $eB "xid related tests ..."

	local xids="0 1 255 256 666"
	local rpat="0 .... 1 ..^. 255 ..^."
	local wpat="0 .... 1 ..^. 255 ..^."

	eexec 001  	0 "mount -o $mopt $DEV $MNT 3>&2"	|| return
	eexec 002      32 "mount -o remount,$xopt $DEV 3>&2"
	eval		  "umount $DEV 3>&2"

	eexec 011  	0 "mount -o $xopt $DEV $MNT 3>&2"	|| return
	eexec 012	0 "do_xid_touch $MNT $xids"
	eexec 014	0 "do_xid_verify $MNT $xids"
	eexec 015	0 "do_xid_read $MNT $rpat"
	eexec 019  	0 "umount $DEV 3>&2"

	[ $terse -ne 0 ] && echo ""

	eval		  "mount -o $xopt $DEV $MNT 3>&2"	|| return
	eexec 020  	0 "do_xid_verify $MNT $xids"
	eexec 021	0 "mount -o remount,$xopt $DEV 3>&2"
	eexec 022  	0 "do_xid_verify $MNT $xids"
	eexec 023	0 "mount -o remount,$mopt $DEV 3>&2"
	eexec 024  	0 "do_xid_verify $MNT $xids"
	eexec 025	0 "do_xid_read $MNT $rpat"
	eexec 026  	0 "do_xid_verify $MNT $xids"
	eexec 027	0 "do_xid_write $MNT $wpat"
	eexec 028  	0 "do_xid_verify $MNT $xids"

	[ $terse -ne 0 ] && echo ""

	local xids="200 201 256 254 777"
	local rpat="200 ..^."
	local wpat="200 ..^. 201 ..^."

	eexec 033  	0 "do_xid_change $MNT $xids"
	eexec 034  	0 "do_xid_verify $MNT $xids"
	eexec 035	0 "do_xid_read $MNT $rpat"
	eexec 037	0 "do_xid_write $MNT $wpat"
	eval		  "umount $DEV 3>&2"
	
	[ $terse -ne 0 ] && echo ""
	X="X"
    }

    [ $test_xattr -gt 0 ] && {

    	eecho $eB "xattr related tests ..."
	
	case $version in
	  24)
	    local attr_B="---BU--" attr_b="---bu--"
	    local attr_U="----U--" attr_u="----u--"
	    local lsattr_B="-+(-)-t*(-)"
	    local lsattr_U="-+(-)-i-+(-)-t*(-)"
	    ;;
	  *)
	    local attr_B="---Bui-" attr_b="---bui-"
	    local attr_U="----UI-" attr_u="----ui-"
	    local lsattr_B="-+(-)-"
	    local lsattr_U="-+(-)-i-+(-)?(E)+(-)"
	    ;;
	esac

	local dpath="$MNT/dir_$$"
	local fpath="$MNT/file_$$"

	eexec 101  	0 "mount -o $mopt $DEV $MNT 3>&2"	|| return
	eval		  "mkdir -p $dpath"
	eexec 102	0 "setattr --barrier $dpath"
	eexec 103	0 "do_xattr_verify $MNT showattr $dpath $attr_B"
	eexec 104	0 "do_xattr_verify $MNT lsattr $dpath $lsattr_B"
	eexec 106	0 "do_xattr_barrier $MNT $dpath .^^"
	eexec 108	0 "setattr --~barrier $dpath"
	eexec 109	0 "do_xattr_verify $MNT showattr $dpath $attr_b"
	eval		  "rmdir $dpath"

	[ $terse -ne 0 ] && echo ""

	eval		  "echo five > $fpath"
	eval		  "ln $fpath $fpath.x"
	eexec 112	0 "setattr --iunlink $fpath"
	eexec 113	0 "do_xattr_verify $MNT showattr $fpath $attr_U"
	eexec 114	0 "do_xattr_verify $MNT lsattr $fpath $lsattr_U"
	eexec 115	0 "ln $fpath $fpath.y"
	eexec 116	0 "do_xattr_iunlink $MNT $fpath ^^^"
	eexec 117	0 "go_xid 2 rm -f $fpath"
	eexec 118	0 "setattr --~iunlink $fpath.x"
	eexec 119	0 "do_xattr_verify $MNT showattr $fpath.x $attr_u"
	eval		  "rm -f $fpath.y $fpath.x"

	[ $terse -ne 0 ] && echo ""

	eval		  "mkdir -p $dpath"
	eval		  "setattr --barrier $dpath"
	eval		  "echo five > $fpath"
	eval		  "setattr --iunlink $fpath"
	eval  		  "umount $DEV 3>&2"
	eval  		  "mount -o $mopt $DEV $MNT 3>&2"	|| return
	eexec 121	0 "do_xattr_verify $MNT showattr $dpath $attr_B"
	eexec 122	0 "do_xattr_verify $MNT lsattr $dpath $lsattr_B"
	eexec 123	0 "do_xattr_verify $MNT showattr $fpath $attr_U"
	eexec 124	0 "do_xattr_verify $MNT lsattr $fpath $lsattr_U"
	eexec 199  	0 "umount $DEV 3>&2"

	[ $terse -ne 0 ] && echo ""
    }
    return 0
}

verbose=0
terse=0
test_xid=0
test_xattr=1
version="26"
tagxid="tagxid"

DEV=""
MNT="/test"
NFS="127.0.0.1:/nfs"
FSL="ext2,ext3,xfs,reiser,jfs"

mntopt="rw"
nfsopt="vers=3,hard,intr,tcp,sync"


while getopts ":hlnotvxD:F:M:N:O:" option; do
  case $option in
    h)  # help
        cat << EOF
Usage: $cmdname [OPTION]...

  -h        help
  -l        legacy barrier code
  -n        no color codes
  -t        terse output
  -o <opt>  mount options [$mntopt]
  -v        be verbose
  -x        xid tagging tests
  -D <dev>  device to use [$DEV]
  -F <fs>   filesystems
  -M <mnt>  mount point [$MNT]
  -N <src>  nfs source [$NFS]
  -O <opt>  nfs options [$nfsopt]

examples:
  
  $cmdname -F ext2  # check fs
  $cmdname -v       # verbose test
  
EOF
        exit 0
        ;;
    v)  # be verbose 
        verbose=$(( verbose + 1 ))
        ;;
    x)  # test xid tagging
        test_xid=1;
        ;;
    l)  # old barrier code
        version="24";
	# tagxid="tagctx";
        ;;
    n)  # no color
	eR='';eG='';eY='';eB='';eN=''
        nocolor=1;
        ;;
    o)  # mount options
        mntopt="$OPTARG"
        ;;
    t)  # terse output
        terse=1
        ;;
    D)  # device 
        DEV="$OPTARG"
        ;;
    F)  # single fs
        FSL="$OPTARG"
        ;;
    M)  # mount point
        MNT="$OPTARG"
        ;;
    N)  # nfs options
        NFS="$OPTARG"
        ;;
    O)  # nfs mount options
        nfsopt="$OPTARG"
        ;;
  esac
done
shift $(($OPTIND - 1))

if [ -z "$DEV" ]; then
    eecho $eR "Please specify a device!" >&2
    exit 1
elif [ ! -e "$DEV" ]; then
    eecho $eR "Please specify a valid device!" >&2
    exit 1
fi

if [ ! -d "$MNT" ]; then
    eecho $eR "Please specify an existing mountpoint or create \"$MNT\"!" >&2
    exit 1
fi

outdev="/dev/null"
[ $verbose -gt 1 ] && outdev="/dev/stdout"
errdev="/dev/null"
[ $verbose -gt 0 ] && errdev="/dev/stderr"


eecho $eY "Linux-VServer FS Test [V0.13] Copyright (C) 2005-2006 H.Poetzl"

KERN=`uname -srm`
CHCV=`chcontext --version 2>&1`
CHCO=`echo -e "$CHCV" |
    sed -n '/--\|version/ {s/.*\ \([0-9][0-9.]*\).*/\1/g;p;q;}'`

INFO=(`sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo '<none>'`)
case ${INFO[2]:1:1} in
  0) TAGI="none"	;;
  1) TAGI="uid16"	;;
  2) TAGI="gid16"	;;
  3) TAGI="ugid24"	;;
  4) TAGI="intern"	;;
  5) TAGI="runtime"	;;
  *) TAGI="unknown"	;;
esac

echo "$KERN/$CHCO"
echo "VCI:  $INFO ${INFO[1]} ${INFO[2]} ($TAGI)"

fsl=`echo $FSL | tr ',' ' '`

for fs in $fsl; do
    echo "---"
    eecho $eY "testing $fs filesystem ..."
    case $fs in 
    nfs)
	do_test nfs "$nfsopt,$mntopt"
	continue
	;;
	
    ext2|ext3)
	mkfsopt="";
	testopt="$mntopt";
	;;
    xfs|jfs)
	mkfsopt="-f";
	testopt="$mntopt";
	;;
    reiser*)
	mkfsopt="-f";
	testopt="attrs,$mntopt"
	fs="reiserfs";
	;;
    *)
	eecho $eR "unknown filesystem $fs."
	continue
	;;
    esac
	
    mkfs.$fs $mkfsopt $DEV 1>$outdev 2>$errdev 
    rc=$?; ececho $rc "[000]"
    [ $rc -gt 0 ] && echo "($fs format failed)" && continue
    do_test "$fs" "$testopt"
    [ $terse -ne 0 ] && echo "" 
done

