xfs: test inode allocation with fragmented free space
[xfstests-dev.git] / common / rc
index a761833caacaac84c5a03e75d65442ceda19499b..0562360134393739026dd40cf212b5a9ac3bf127 100644 (file)
--- a/common/rc
+++ b/common/rc
 
 BC=$(which bc 2> /dev/null) || BC=
 
+# Valid test names start with 3 digits "NNN":
+#  "[0-9]\{3\}"
+# followed by an optional "-":
+#  "-\?"
+# followed by an optional combination of alphanumeric and "-" chars:
+#  "[[:alnum:]-]*"
+# e.g. 999-the-mark-of-fstests
+#
+VALID_TEST_NAME="[0-9]\{3\}-\?[[:alnum:]-]*"
+
 _require_math()
 {
        if [ -z "$BC" ]; then
@@ -44,7 +54,7 @@ dd()
 {
    if [ "$HOSTOS" == "Linux" ]
    then        
-       command dd --help | grep noxfer > /dev/null 2>&1
+       command dd --help 2>&1 | grep noxfer >/dev/null
        
        if [ "$?" -eq 0 ]
            then
@@ -74,98 +84,13 @@ _md5_checksum()
 
 # ls -l w/ selinux sometimes puts a dot at the end:
 # -rwxrw-r--. id1 id2 file1
+# Also filter out lost+found directory on extN file system if present
 
 _ls_l()
 {
-       ls -l $* | sed "s/\(^[-rwxdlbcpsStT]*\)\. /\1 /"
-}
-
-_mount_opts()
-{
-    # SELinux adds extra xattrs which can mess up our expected output.
-    # So, mount with a context, and they won't be created
-    # nfs_t is a "liberal" context so we can use it.
-    if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then
-       SELINUX_MOUNT_OPTIONS="-o context=system_u:object_r:nfs_t:s0"
-       export SELINUX_MOUNT_OPTIONS
-    fi
-
-    case $FSTYP in
-    xfs)
-       export MOUNT_OPTIONS=$XFS_MOUNT_OPTIONS
-       ;;
-    udf)
-       export MOUNT_OPTIONS=$UDF_MOUNT_OPTIONS
-       ;;
-    nfs)
-       export MOUNT_OPTIONS=$NFS_MOUNT_OPTIONS
-       ;;
-    ext2|ext3|ext4|ext4dev)
-       # acls & xattrs aren't turned on by default on ext$FOO
-       export MOUNT_OPTIONS="-o acl,user_xattr $EXT_MOUNT_OPTIONS"
-       ;;
-    reiserfs)
-       # acls & xattrs aren't turned on by default on reiserfs
-       export MOUNT_OPTIONS="-o acl,user_xattr $REISERFS_MOUNT_OPTIONS"
-       ;;
-    gfs2)
-       # acls aren't turned on by default on gfs2
-       export MOUNT_OPTIONS="-o acl $GFS2_MOUNT_OPTIONS"
-       ;;
-    *)
-       ;;
-    esac
-}
-
-_mkfs_opts()
-{
-    case $FSTYP in
-    xfs)
-       export MKFS_OPTIONS=$XFS_MKFS_OPTIONS
-       ;;
-    udf)
-       [ ! -z "$udf_fsize" ] && \
-           UDF_MKFS_OPTIONS="$UDF_MKFS_OPTIONS -s $udf_fsize"
-       export MKFS_OPTIONS=$UDF_MKFS_OPTIONS
-       ;;
-    nfs)
-       export MKFS_OPTIONS=$NFS_MKFS_OPTIONS
-       ;;
-    reiserfs)
-       export MKFS_OPTIONS="$REISERFS_MKFS_OPTIONS -q"
-       ;;
-    gfs2)
-       export MKFS_OPTIONS="$GFS2_MKFS_OPTIONS -O -p lock_nolock"
-       ;;
-    jfs)
-       export MKFS_OPTIONS="$JFS_MKFS_OPTIONS -q"
-       ;;
-    *)
-       ;;
-    esac
-}
-
-_fsck_opts()
-{
-    case $FSTYP in
-    ext2|ext3|ext4|ext4dev)
-       export FSCK_OPTIONS="-nf"
-       ;;
-    reiserfs)
-       export FSCK_OPTIONS="--yes"
-       ;;
-    *)
-       export FSCK_OPTIONS="-n"
-       ;;
-    esac
+       ls -l $* | sed "s/\(^[-rwxdlbcpsStT]*\)\. /\1 /" | grep -v 'lost+found'
 }
 
-[ -z "$FSTYP" ] && FSTYP=xfs
-[ -z "$MOUNT_OPTIONS" ] && _mount_opts
-[ -z "$MKFS_OPTIONS" ] && _mkfs_opts
-[ -z "$FSCK_OPTIONS" ] && _fsck_opts
-
-
 # we need common/config
 if [ "$iam" != "check" ]
 then
@@ -190,8 +115,19 @@ case "$FSTYP" in
     btrfs)
         [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
         ;;
+    ext4)
+        [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
+        ;;
+    f2fs)
+        [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
+        ;;
     nfs)
         ;;
+    cifs)
+        ;;
+    reiser4)
+        [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
+        ;;
 esac
 
 # make sure we have a standard umask
@@ -303,12 +239,28 @@ _test_mount()
     _mount -t $FSTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
 }
 
+_test_remount()
+{
+    $UMOUNT_PROG $TEST_DEV
+    _test_mount
+}
+
 _scratch_mkfs_options()
 {
     _scratch_options mkfs
     echo $SCRATCH_OPTIONS $MKFS_OPTIONS $* $SCRATCH_DEV
 }
 
+_scratch_metadump()
+{
+       dumpfile=$1
+       options=
+
+       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+               options="-l $SCRATCH_LOGDEV"
+
+       xfs_metadump $options $SCRATCH_DEV $dumpfile
+}
 
 _setup_large_xfs_fs()
 {
@@ -352,53 +304,81 @@ _setup_large_xfs_fs()
        return 0
 }
 
+_scratch_mkfs_xfs_opts()
+{
+       mkfs_opts=$*
+
+       # remove crc related mkfs options if mkfs.xfs doesn't support v5 xfs
+       if [ -n "$XFS_MKFS_HAS_NO_META_SUPPORT" ]; then
+               mkfs_opts=`echo $mkfs_opts | sed "s/-m\s\+crc=.//"`
+       fi
+
+       _scratch_options mkfs
+
+       $MKFS_XFS_PROG $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
+}
+
+
+_scratch_mkfs_xfs_supported()
+{
+       mkfs_opts=$*
+
+       _scratch_options mkfs
+
+       $MKFS_XFS_PROG -N $MKFS_OPTIONS $SCRATCH_OPTIONS $mkfs_opts $SCRATCH_DEV
+}
+
 _scratch_mkfs_xfs()
 {
-    # extra mkfs options can be added by tests
-    local extra_mkfs_options=$*
+       # extra mkfs options can be added by tests
+       local extra_mkfs_options=$*
 
-    local tmp_dir=/tmp/
+       local tmp_dir=/tmp/
 
-    _scratch_options mkfs
+       # save mkfs output in case conflict means we need to run again.
+       # only the output for the mkfs that applies should be shown
+       _scratch_mkfs_xfs_opts $MKFS_OPTIONS $extra_mkfs_options \
+               2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+       local mkfs_status=$?
 
-    # save mkfs output in case conflict means we need to run again.
-    # only the output for the mkfs that applies should be shown
-    $MKFS_XFS_PROG $SCRATCH_OPTIONS $MKFS_OPTIONS $extra_mkfs_options $SCRATCH_DEV \
-        2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
-    local mkfs_status=$?
-
-    # a mkfs failure may be caused by conflicts between
-    # $MKFS_OPTIONS and $extra_mkfs_options
-
-    if [ $mkfs_status -ne 0 -a ! -z "$extra_mkfs_options" ]; then
-        echo "** mkfs failed with extra mkfs options added to \"$MKFS_OPTIONS\" by test $seq **" \
-            >>$seqres.full
-        echo "** attempting to mkfs using only test $seq options: $extra_mkfs_options **" \
-            >>$seqres.full
-        # running mkfs again. overwrite previous mkfs output files
-        $MKFS_XFS_PROG $SCRATCH_OPTIONS $extra_mkfs_options $SCRATCH_DEV \
-            2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
-        mkfs_status=$?
-    fi
 
-    if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
-       # manually parse the mkfs output to get the fs size in bytes
-       local fs_size
-       fs_size=`cat $tmp_dir.mkfsstd | perl -ne '
-           if (/^data\s+=\s+bsize=(\d+)\s+blocks=(\d+)/) {
-               my $size = $1 * $2;
-               print STDOUT "$size\n";
-           }'`
-       _setup_large_xfs_fs $fs_size
-       mkfs_status=$?
-    fi
+       # a mkfs failure may be caused by conflicts between
+       # $MKFS_OPTIONS and $extra_mkfs_options
+       if [ $mkfs_status -ne 0 -a ! -z "$extra_mkfs_options" ]; then
+               (
+               echo -n "** mkfs failed with extra mkfs options "
+               echo "added to \"$MKFS_OPTIONS\" by test $seq **"
+               echo -n "** attempting to mkfs using only test $seq "
+               echo "options: $extra_mkfs_options **"
+               ) >> $seqres.full
 
-    # output stored mkfs output
-    cat $tmp_dir.mkfserr >&2
-    cat $tmp_dir.mkfsstd
-    rm -f $tmp_dir.mkfserr $tmp_dir.mkfsstd
+               # running mkfs again. overwrite previous mkfs output files
+               _scratch_mkfs_xfs_opts $extra_mkfs_options \
+                       2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+               local mkfs_status=$?
+       fi
+
+       if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
+               # manually parse the mkfs output to get the fs size in bytes
+               local fs_size
+               fs_size=`cat $tmp_dir.mkfsstd | perl -ne '
+                       if (/^data\s+=\s+bsize=(\d+)\s+blocks=(\d+)/) {
+                               my $size = $1 * $2;
+                               print STDOUT "$size\n";
+                       }'`
+               _setup_large_xfs_fs $fs_size
+               mkfs_status=$?
+       fi
 
-    return $mkfs_status
+       # output stored mkfs output, filtering unnecessary warnings from stderr
+       cat $tmp_dir.mkfsstd
+       cat $tmp_dir.mkfserr | sed \
+               -e '/less than device physical sector/d' \
+               -e '/switching to logical sector/d' \
+               >&2
+       rm -f $tmp_dir.mkfserr $tmp_dir.mkfsstd
+
+       return $mkfs_status
 }
 
 # xfs_check script is planned to be deprecated. But, we want to
@@ -498,14 +478,34 @@ _setup_large_ext4_fs()
        fi
        return 0
 }
+
 _scratch_mkfs_ext4()
 {
+       # extra mkfs options can be added by tests
+       local extra_mkfs_options=$*
+
        local tmp_dir=/tmp/
 
-       /sbin/mkfs -t $FSTYP -- -F $MKFS_OPTIONS $* $SCRATCH_DEV \
+       $MKFS_EXT4_PROG -F $MKFS_OPTIONS $extra_mkfs_options $SCRATCH_DEV \
                        2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
        local mkfs_status=$?
 
+       # a mkfs failure may be caused by conflicts between
+       # $MKFS_OPTIONS and $extra_mkfs_options
+       if [ $mkfs_status -ne 0 -a ! -z "$extra_mkfs_options" ]; then
+               (
+               echo -n "** mkfs failed with extra mkfs options "
+               echo "added to \"$MKFS_OPTIONS\" by test $seq **"
+               echo -n "** attempting to mkfs using only test $seq "
+               echo "options: $extra_mkfs_options **"
+               ) >> $seqres.full
+
+               # running mkfs again. overwrite previous mkfs output files
+               $MKFS_EXT4_PROG -F $extra_mkfs_options $SCRATCH_DEV \
+                               2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+               local mkfs_status=$?
+       fi
+
        if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
                # manually parse the mkfs output to get the fs size in bytes
                fs_size=`cat $tmp_dir.mkfsstd | awk ' \
@@ -526,6 +526,71 @@ _scratch_mkfs_ext4()
        return $mkfs_status
 }
 
+_test_mkfs()
+{
+    case $FSTYP in
+    nfs*)
+       # do nothing for nfs
+       ;;
+    cifs)
+       # do nothing for cifs
+       ;;
+    udf)
+        $MKFS_UDF_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
+       ;;
+    btrfs)
+        $MKFS_BTRFS_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
+       ;;
+    ext2|ext3|ext4)
+       $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* $TEST_DEV
+       ;;
+    *)
+       yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $TEST_DEV
+       ;;
+    esac
+}
+
+_mkfs_dev()
+{
+    case $FSTYP in
+    nfs*)
+       # do nothing for nfs
+       ;;
+    udf)
+        $MKFS_UDF_PROG $MKFS_OPTIONS $* 2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+       ;;
+    btrfs)
+        $MKFS_BTRFS_PROG $MKFS_OPTIONS $* 2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+       ;;
+    ext2|ext3|ext4)
+       $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* \
+               2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+       ;;
+
+    *)
+       yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* \
+               2>$tmp_dir.mkfserr 1>$tmp_dir.mkfsstd
+       ;;
+    esac
+
+    if [ $? -ne 0 ]; then
+       # output stored mkfs output
+       cat $tmp_dir.mkfserr >&2
+       cat $tmp_dir.mkfsstd
+       status=1
+       exit 1
+    fi
+    rm -f $tmp_dir.mkfserr $tmp_dir.mkfsstd
+}
+
+# remove all files in $SCRATCH_MNT, useful when testing on NFS/CIFS
+_scratch_cleanup_files()
+{
+       _scratch_mount
+       rm -rf $SCRATCH_MNT/*
+       _scratch_unmount
+}
+
 _scratch_mkfs()
 {
     case $FSTYP in
@@ -533,7 +598,14 @@ _scratch_mkfs()
         _scratch_mkfs_xfs $*
        ;;
     nfs*)
-       # do nothing for nfs
+       # unable to re-create NFS, just remove all files in $SCRATCH_MNT to
+       # avoid EEXIST caused by the leftover files created in previous runs
+        _scratch_cleanup_files
+       ;;
+    cifs)
+       # unable to re-create CIFS, just remove all files in $SCRATCH_MNT to
+       # avoid EEXIST caused by the leftover files created in previous runs
+        _scratch_cleanup_files
        ;;
     udf)
         $MKFS_UDF_PROG $MKFS_OPTIONS $* $SCRATCH_DEV > /dev/null
@@ -541,22 +613,70 @@ _scratch_mkfs()
     btrfs)
         $MKFS_BTRFS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV > /dev/null
        ;;
+    ext2|ext3)
+       $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* $SCRATCH_DEV
+       ;;
     ext4)
        _scratch_mkfs_ext4 $*
        ;;
+    tmpfs)
+       # do nothing for tmpfs
+       ;;
+    f2fs)
+        $MKFS_F2FS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV > /dev/null
+       ;;
     *)
        yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $SCRATCH_DEV
        ;;
     esac
 }
 
+_scratch_pool_mkfs()
+{
+    case $FSTYP in
+    btrfs)
+        # if dup profile is in mkfs options call _scratch_mkfs instead
+        # because dup profile only works with single device
+        if [[ "$*" =~ dup ]]; then
+            _scratch_mkfs $*
+        else
+            $MKFS_BTRFS_PROG $MKFS_OPTIONS $* $SCRATCH_DEV_POOL > /dev/null
+        fi
+        ;;
+    *)
+        echo "_scratch_pool_mkfs is not implemented for $FSTYP" 1>&2
+        ;;
+    esac
+}
+
 # Create fs of certain size on scratch device
 # _scratch_mkfs_sized <size in bytes> [optional blocksize]
 _scratch_mkfs_sized()
 {
     fssize=$1
     blocksize=$2
+
+    case $FSTYP in
+    xfs)
+       def_blksz=`echo $MKFS_OPTIONS|sed -rn 's/.*-b ?size= ?+([0-9]+).*/\1/p'`
+       ;;
+    ext2|ext3|ext4|ext4dev|udf|btrfs|reiser4)
+       def_blksz=`echo $MKFS_OPTIONS| sed -rn 's/.*-b ?+([0-9]+).*/\1/p'`
+       ;;
+    esac
+
+    [ -n "$def_blksz" ] && blocksize=$def_blksz
     [ -z "$blocksize" ] && blocksize=4096
+
+
+    re='^[0-9]+$'
+    if ! [[ $fssize =~ $re ]] ; then
+        _notrun "error: _scratch_mkfs_sized: fs size \"$fssize\" not an integer."
+    fi
+    if ! [[ $blocksize =~ $re ]] ; then
+        _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
+    fi
+
     blocks=`expr $fssize / $blocksize`
 
     if [ "$HOSTOS" == "Linux" ]; then
@@ -566,14 +686,28 @@ _scratch_mkfs_sized()
 
     case $FSTYP in
     xfs)
-       _scratch_mkfs_xfs -d size=$fssize -b size=$blocksize
+       # don't override MKFS_OPTIONS that set a block size.
+       echo $MKFS_OPTIONS |egrep -q "b?size="
+       if [ $? -eq 0 ]; then
+               _scratch_mkfs_xfs -d size=$fssize
+       else
+               _scratch_mkfs_xfs -d size=$fssize -b size=$blocksize
+       fi
        ;;
     ext2|ext3|ext4|ext4dev)
-       yes | ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
+       ${MKFS_PROG}.$FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
+       ;;
+    udf)
+       $MKFS_UDF_PROG $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
        ;;
     btrfs)
        $MKFS_BTRFS_PROG $MKFS_OPTIONS -b $fssize $SCRATCH_DEV
        ;;
+    reiser4)
+       # mkfs.resier4 requires size in KB as input for creating filesystem
+       $MKFS_REISER4_PROG $MKFS_OPTIONS -y -b $blocksize $SCRATCH_DEV \
+                          `expr $fssize / 1024`
+       ;;
     *)
        _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
        ;;
@@ -730,14 +864,16 @@ _df_device()
        exit 1
     fi
 
+    # Note that we use "==" here so awk doesn't try to interpret an NFS over
+    # IPv6 server as a regular expression.
     $DF_PROG 2>/dev/null | $AWK_PROG -v what=$1 '
-        match($1,what) && NF==1 {
+        ($1==what) && (NF==1) {
             v=$1
             getline
             print v, $0
             exit
         }
-        match($1,what) {
+        ($1==what) {
             print
             exit
         }
@@ -839,11 +975,11 @@ _is_block_dev()
 
     _dev=$1
     if [ -L "${_dev}" ]; then
-        _dev=`readlink -f ${_dev}`
+        _dev=`readlink -f "${_dev}"`
     fi
 
     if [ -b "${_dev}" ]; then
-        src/lstat64 ${_dev} | $AWK_PROG '/Device type:/ { print $9 }'
+        src/lstat64 "${_dev}" | $AWK_PROG '/Device type:/ { print $9 }'
     fi
 }
 
@@ -892,12 +1028,15 @@ _do()
     return $ret
 }
 
-# bail out, setting up .notrun file
+# bail out, setting up .notrun file. Need to kill the filesystem check files
+# here, otherwise they are set incorrectly for the next test.
 #
 _notrun()
 {
     echo "$*" > $seqres.notrun
     echo "$seq not run: $*"
+    rm -f ${RESULT_DIR}/require_test
+    rm -f ${RESULT_DIR}/require_scratch
     status=0
     exit
 }
@@ -927,6 +1066,7 @@ _supported_fs()
     _notrun "not suitable for this filesystem type: $FSTYP"
 }
 
+
 # tests whether $FSTYP is one of the supported OSes for a test
 #
 _supported_os()
@@ -943,23 +1083,41 @@ _supported_os()
 }
 
 # this test needs a scratch partition - check we're ok & unmount it
-#
-_require_scratch()
+# No post-test check of the device is required. e.g. the test intentionally
+# finishes the test with the filesystem in a corrupt state
+_require_scratch_nocheck()
 {
     case "$FSTYP" in
        nfs*)
-                echo $SCRATCH_DEV | grep -q ":" > /dev/null 2>&1
-                if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]
-                then
-                    _notrun "this test requires a valid \$SCRATCH_DEV"
-                fi
-                ;;
+               echo $SCRATCH_DEV | grep -q ":/" > /dev/null 2>&1
+               if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$SCRATCH_DEV"
+               fi
+               if [ ! -d "$SCRATCH_MNT" ]; then
+                       _notrun "this test requires a valid \$SCRATCH_MNT"
+               fi
+               ;;
+       cifs)
+               echo $SCRATCH_DEV | grep -q "//" > /dev/null 2>&1
+               if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$SCRATCH_DEV"
+               fi
+               if [ ! -d "$SCRATCH_MNT" ]; then
+                    _notrun "this test requires a valid \$SCRATCH_MNT"
+               fi
+               ;;
+       tmpfs)
+               if [ -z "$SCRATCH_DEV" -o ! -d "$SCRATCH_MNT" ];
+               then
+                   _notrun "this test requires a valid \$SCRATCH_MNT and unique $SCRATCH_DEV"
+               fi
+               ;;
        *)
-                if [ -z "$SCRATCH_DEV" -o "`_is_block_dev $SCRATCH_DEV`" = "" ]
+                if [ -z "$SCRATCH_DEV" -o "`_is_block_dev "$SCRATCH_DEV"`" = "" ]
                 then
                     _notrun "this test requires a valid \$SCRATCH_DEV"
                 fi
-                if [ "`_is_block_dev $SCRATCH_DEV`" = "`_is_block_dev $TEST_DEV`" ]
+                if [ "`_is_block_dev "$SCRATCH_DEV"`" = "`_is_block_dev "$TEST_DEV"`" ]
                 then
                     _notrun "this test requires a valid \$SCRATCH_DEV"
                 fi
@@ -971,10 +1129,12 @@ _require_scratch()
     esac
 
     # mounted?
-    if _mount | grep -q $SCRATCH_DEV
+    # Note that we use -F here so grep doesn't try to interpret an NFS over
+    # IPv6 server as a regular expression.
+    if _mount | grep -F -q $SCRATCH_DEV
     then
         # if it's mounted, make sure its on $SCRATCH_MNT
-        if ! _mount | grep $SCRATCH_DEV | grep -q $SCRATCH_MNT
+        if ! _mount | grep -F $SCRATCH_DEV | grep -q $SCRATCH_MNT
         then
             echo "\$SCRATCH_DEV is mounted but not on \$SCRATCH_MNT - aborting"
             exit 1
@@ -986,6 +1146,81 @@ _require_scratch()
             exit 1
         fi
     fi
+    rm -f ${RESULT_DIR}/require_scratch
+}
+
+# we need the scratch device and it should be checked post test.
+_require_scratch()
+{
+       _require_scratch_nocheck
+       touch ${RESULT_DIR}/require_scratch
+}
+
+
+# this test needs a test partition - check we're ok & mount it
+#
+_require_test()
+{
+    case "$FSTYP" in
+       nfs*)
+               echo $TEST_DEV | grep -q ":/" > /dev/null 2>&1
+               if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$TEST_DIR"
+               fi
+               if [ ! -d "$TEST_DIR" ]; then
+                       _notrun "this test requires a valid \$TEST_DIR"
+               fi
+               ;;
+       cifs)
+               echo $TEST_DEV | grep -q "//" > /dev/null 2>&1
+               if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$TEST_DEV"
+               fi
+               if [ ! -d "$TEST_DIR" ]; then
+                    _notrun "this test requires a valid \$TEST_DIR"
+               fi
+               ;;
+       tmpfs)
+               if [ -z "$TEST_DEV" -o ! -d "$TEST_DIR" ];
+               then
+                   _notrun "this test requires a valid \$TEST_DIR and unique $TEST_DEV"
+               fi
+               ;;
+       *)
+                if [ -z "$TEST_DEV" ] || [ "`_is_block_dev "$TEST_DEV"`" = "" ]
+                then
+                    _notrun "this test requires a valid \$TEST_DEV"
+                fi
+                if [ "`_is_block_dev "$SCRATCH_DEV"`" = "`_is_block_dev "$TEST_DEV"`" ]
+                then
+                    _notrun "this test requires a valid \$TEST_DEV"
+                fi
+               if [ ! -d "$TEST_DIR" ]
+               then
+                    _notrun "this test requires a valid \$TEST_DIR"
+               fi
+                ;;
+    esac
+
+    # mounted?
+    # Note that we use -F here so grep doesn't try to interpret an NFS over
+    # IPv6 server as a regular expression.
+    if _mount | grep -F -q $TEST_DEV
+    then
+        # if it's mounted, make sure its on $TEST_DIR
+        if ! _mount | grep -F $TEST_DEV | grep -q $TEST_DIR
+        then
+            echo "\$TEST_DEV is mounted but not on \$TEST_DIR - aborting"
+            exit 1
+        fi
+    else
+       out=`_mount_or_remount_rw "$MOUNT_OPTIONS" $TEST_DEV $TEST_DIR`
+       if [ $? -ne 1 ]; then
+               echo $out
+               exit 1
+       fi
+    fi
+    touch ${RESULT_DIR}/require_test
 }
 
 # this test needs a logdev
@@ -1059,43 +1294,179 @@ _require_realtime()
 # this test requires that a specified command (executable) exists
 # $1 - command, $2 - name for error message
 #
+# Note: the command string might have parameters, so strip them before checking
+# whether it is executable.
 _require_command()
 {
-    [ -n "$1" ] && _cmd="$1" || _cmd="$2"
-    [ -n "$1" -a -x "$1" ] || _notrun "$_cmd utility required, skipped this test"
+       if [ $# -eq 2 ]; then
+               _name="$2"
+       elif [ $# -eq 1 ]; then
+               _name="$1"
+       else
+               _fail "usage: _require_command <command> [<name>]"
+       fi
+
+       _command=`echo "$1" | awk '{ print $1 }'`
+       if [ ! -x "$_command" ]; then
+               _notrun "$_name utility required, skipped this test"
+       fi
+}
+
+# this test requires the device to be valid block device
+# $1 - device
+_require_block_device()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _require_block_device <dev>" 1>&2
+               exit 1
+       fi
+       if [ "`_is_block_dev "$1"`" == "" ]; then
+               _notrun "require $1 to be valid block disk"
+       fi
+}
+
+# brd based ram disks erase the device when they receive a flush command when no
+# active references are present. This causes problems for DM devices sitting on
+# top of brd devices as DM doesn't hold active references to the brd device.
+_require_sane_bdev_flush()
+{
+       echo $1 | grep -q "^/dev/ram[0-9]\+$"
+       if [ $? -eq 0 ]; then
+               _notrun "This test requires a sane block device flush"
+       fi
 }
 
 # this test requires the device mapper flakey target
 #
 _require_dm_flakey()
 {
-    _require_command $DMSETUP_PROG
+       # require SCRATCH_DEV to be a valid block device with sane BLKFLSBUF
+       # behaviour
+       _require_block_device $SCRATCH_DEV
+       _require_sane_bdev_flush $SCRATCH_DEV
+       _require_command "$DMSETUP_PROG" dmsetup
 
-    modprobe dm-flakey >/dev/null 2>&1
-    $DMSETUP_PROG targets | grep flakey >/dev/null 2>&1
-    if [ $? -eq 0 ]
-    then
-       :
-    else
-       _notrun "This test requires dm flakey support"
-    fi
+       modprobe dm-flakey >/dev/null 2>&1
+       $DMSETUP_PROG targets | grep flakey >/dev/null 2>&1
+       if [ $? -ne 0 ]; then
+               _notrun "This test requires dm flakey support"
+       fi
+}
+
+_require_dm_snapshot()
+{
+       _require_block_device $SCRATCH_DEV
+       _require_sane_bdev_flush $SCRATCH_DEV
+       _require_command "$DMSETUP_PROG" dmsetup
+       modprobe dm-snapshot >/dev/null 2>&1
+       $DMSETUP_PROG targets | grep -q snapshot
+       if [ $? -ne 0 ]; then
+               _notrun "This test requires dm snapshot support"
+       fi
 }
 
-# this test requires the projid32bit feature to be available in
-# mkfs.xfs
+# this test requires the projid32bit feature to be available in mkfs.xfs.
 #
 _require_projid32bit()
 {
-        _scratch_mkfs_xfs -f -i projid32bit=0 2>&1 >/dev/null \
+       _scratch_mkfs_xfs_supported -i projid32bit=1 >/dev/null 2>&1 \
           || _notrun "mkfs.xfs doesn't have projid32bit feature"
 }
 
-# this test requires that external log/realtime devices are not in use
-#
-_require_nonexternal()
+_require_projid16bit()
 {
-    [ "$USE_EXTERNAL" = yes ] && \
-       _notrun "External device testing in progress, skipped this test"
+       _scratch_mkfs_xfs_supported -i projid32bit=0 >/dev/null 2>&1 \
+          || _notrun "16 bit project IDs not supported on $SCRATCH_DEV"
+}
+
+# this test requires the crc feature to be available in mkfs.xfs
+#
+_require_xfs_mkfs_crc()
+{
+       _scratch_mkfs_xfs_supported -m crc=1 >/dev/null 2>&1 \
+          || _notrun "mkfs.xfs doesn't have crc feature"
+}
+
+# this test requires the xfs kernel support crc feature
+#
+_require_xfs_crc()
+{
+       _scratch_mkfs_xfs -m crc=1 >/dev/null 2>&1
+       _scratch_mount >/dev/null 2>&1 \
+          || _notrun "Kernel doesn't support crc feature"
+       umount $SCRATCH_MNT
+}
+
+# this test requires the bigalloc feature to be available in mkfs.ext4
+#
+_require_ext4_mkfs_bigalloc()
+{
+       $MKFS_EXT4_PROG -F -O bigalloc -n $SCRATCH_DEV 512m >/dev/null 2>&1 \
+          || _notrun "mkfs.ext4 doesn't have bigalloc feature"
+}
+
+# this test requires the ext4 kernel support bigalloc feature
+#
+_require_ext4_bigalloc()
+{
+       $MKFS_EXT4_PROG -F -O bigalloc $SCRATCH_DEV 512m >/dev/null 2>&1
+       _scratch_mount >/dev/null 2>&1 \
+          || _notrun "Ext4 kernel doesn't support bigalloc feature"
+       umount $SCRATCH_MNT
+}
+
+# this test requires the finobt feature to be available in mkfs.xfs
+#
+_require_xfs_mkfs_finobt()
+{
+       _scratch_mkfs_xfs_supported -m crc=1,finobt=1 >/dev/null 2>&1 \
+          || _notrun "mkfs.xfs doesn't have finobt feature"
+}
+
+# this test requires the xfs kernel support finobt feature
+#
+_require_xfs_finobt()
+{
+       _scratch_mkfs_xfs -m crc=1,finobt=1 >/dev/null 2>&1
+       _scratch_mount >/dev/null 2>&1 \
+          || _notrun "Kernel doesn't support finobt feature"
+       umount $SCRATCH_MNT
+}
+
+# this test requires xfs sysfs attribute support
+#
+_require_xfs_sysfs()
+{
+       attr=$1
+       sysfsdir=/sys/fs/xfs
+
+       if [ ! -e $sysfsdir ]; then
+               _notrun "no kernel support for XFS sysfs attributes"
+       fi
+
+       if [ ! -z $1 ] && [ ! -e $sysfsdir/$attr ]; then
+               _notrun "sysfs attribute '$attr' is not supported"
+       fi
+}
+
+# this test requires the xfs sparse inode feature
+#
+_require_xfs_sparse_inodes()
+{
+       _scratch_mkfs_xfs_supported -m crc=1 -i sparse > /dev/null 2>&1 \
+               || _notrun "mkfs.xfs does not support sparse inodes"
+       _scratch_mkfs_xfs -m crc=1 -i sparse > /dev/null 2>&1
+       _scratch_mount >/dev/null 2>&1 \
+               || _notrun "kernel does not support sparse inodes"
+       umount $SCRATCH_MNT
+}
+
+# this test requires that external log/realtime devices are not in use
+#
+_require_nonexternal()
+{
+    [ "$USE_EXTERNAL" = yes ] && \
+       _notrun "External device testing in progress, skipped this test"
 }
 
 # this test requires that a (specified) aio-dio executable exists
@@ -1111,6 +1482,7 @@ _require_aiodio()
         AIO_TEST=src/aio-dio-regress/$1
         [ -x $AIO_TEST ] || _notrun "$AIO_TEST not built"
     fi
+    _require_odirect
 }
 
 # run an aio-dio program
@@ -1200,53 +1572,54 @@ _user_do()
     fi
 }
 
-# check that xfs_io, kernel, and filesystem all support zero
-_require_xfs_io_zero()
+_require_xfs_io_command()
 {
-       testio=`$XFS_IO_PROG -c "zero help" 2>&1`
-       echo $testio | grep -q 'command "zero" not found' && \
-               _notrun "zero command not supported"
-}
+       if [ $# -ne 1 ]
+       then
+               echo "Usage: _require_xfs_io_command command" 1>&2
+               exit 1
+       fi
+       command=$1
 
-# check that xfs_io, glibc, kernel, and filesystem all (!) support
-# fallocate
-#
-_require_xfs_io_falloc()
-{
-       testfile=$TEST_DIR/$$.falloc
-       testio=`$XFS_IO_PROG -F -f -c "falloc 0 1m" $testfile 2>&1`
-       rm -f $testfile 2>&1 > /dev/null
-       echo $testio | grep -q "not found" && \
-               _notrun "xfs_io fallocate support is missing"
-       echo $testio | grep -q "Operation not supported" && \
-               _notrun "xfs_io fallocate command failed (old kernel/wrong fs?)"
-}
+       testfile=$TEST_DIR/$$.xfs_io
+       case $command in
+       "falloc" )
+               testio=`$XFS_IO_PROG -F -f -c "falloc 0 1m" $testfile 2>&1`
+               ;;
+       "fpunch" | "fcollapse" | "zero" | "fzero" | "finsert" )
+               testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
+                       -c "$command 4k 8k" $testfile 2>&1`
+               ;;
+       "fiemap")
+               testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
+                       -c "fiemap -v" $testfile 2>&1`
+               ;;
+       "flink" )
+               testio=`$XFS_IO_PROG -T -F -c "flink $testfile" \
+                       $TEST_DIR 2>&1`
+               echo $testio | egrep -q "invalid option|Is a directory" && \
+                       _notrun "xfs_io $command support is missing"
+               ;;
+       *)
+               testio=`$XFS_IO_PROG -c "$command help" 2>&1`
+       esac
 
-# check that xfs_io, kernel and filesystem all support fallocate with hole
-# punching
-_require_xfs_io_falloc_punch()
-{
-       testfile=$TEST_DIR/$$.falloc
-       testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
-               -c "fpunch 4k 8k" $testfile 2>&1`
        rm -f $testfile 2>&1 > /dev/null
        echo $testio | grep -q "not found" && \
-               _notrun "xfs_io fallocate punch support is missing"
+               _notrun "xfs_io $command support is missing"
        echo $testio | grep -q "Operation not supported" && \
-               _notrun "xfs_io fallocate punch command failed (no fs support?)"
+               _notrun "xfs_io $command failed (old kernel/wrong fs?)"
 }
 
-# check that xfs_io, kernel and filesystem support fiemap
-_require_xfs_io_fiemap()
+# check that kernel and filesystem support direct I/O
+_require_odirect()
 {
-       testfile=$TEST_DIR/$$.fiemap
-       testio=`$XFS_IO_PROG -F -f -c "pwrite 0 20k" -c "fsync" \
-               -c "fiemap -v" $testfile 2>&1`
-       rm -f $testfile 2>&1 > /dev/null
-       echo $testio | grep -q "not found" && \
-               _notrun "xfs_io fiemap support is missing"
-       echo $testio | grep -q "Operation not supported" && \
-               _notrun "xfs_io fiemap command failed (no fs support?)"
+       testfile=$TEST_DIR/$$.direct
+       $XFS_IO_PROG -F -f -d -c "pwrite 0 20k" $testfile > /dev/null 2>&1
+       if [ $? -ne 0 ]; then
+               _notrun "O_DIRECT is not supported"
+       fi
+       rm -f $testfile 2>&1 > /dev/null
 }
 
 # Check that a fs has enough free space (in 1024b blocks)
@@ -1447,7 +1820,10 @@ _check_generic_filesystem()
 
     if [ $ok -eq 0 ]; then
        status=1
-       exit 1
+       if [ "$iam" != "check" ]; then
+               exit 1
+       fi
+       return 1
     fi
 
     return 0
@@ -1546,7 +1922,10 @@ _check_xfs_filesystem()
 
     if [ $ok -eq 0 ]; then
        status=1
-       exit 1
+       if [ "$iam" != "check" ]; then
+               exit 1
+       fi
+       return 1
     fi
 
     return 0
@@ -1590,9 +1969,9 @@ _check_udf_filesystem()
     sleep 1 # Due to a problem with time stamps in udf_test
     $here/src/udf_test $OPT_ARG $device | tee $seqres.checkfs | egrep "Error|Warning" | \
        _udf_test_known_error_filter | \
-       egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" | \
-       sed "s/^.*$/Warning UDF Verifier reported errors see $seqres.checkfs./g"
-
+       egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
+        echo "Warning UDF Verifier reported errors see $seqres.checkfs." && return 1
+    return 0
 }
 
 _check_xfs_test_fs()
@@ -1657,7 +2036,10 @@ _check_btrfs_filesystem()
 
     if [ $ok -eq 0 ]; then
        status=1
-       exit 1
+       if [ "$iam" != "check" ]; then
+               exit 1
+       fi
+       return 1
     fi
 
     return 0
@@ -1672,12 +2054,18 @@ _check_test_fs()
     nfs)
        # no way to check consistency for nfs
        ;;
+    cifs)
+       # no way to check consistency for cifs
+       ;;
     udf)
        # do nothing for now
        ;;
     btrfs)
        _check_btrfs_filesystem $TEST_DEV
        ;;
+    tmpfs)
+       # no way to check consistency for tmpfs
+       ;;
     *)
        _check_generic_filesystem $TEST_DEV
        ;;
@@ -1707,9 +2095,15 @@ _check_scratch_fs()
     nfs*)
        # Don't know how to check an NFS filesystem, yet.
        ;;
+    cifs)
+       # Don't know how to check a CIFS filesystem, yet.
+       ;;
     btrfs)
        _check_btrfs_filesystem $device
        ;;
+    tmpfs)
+       # no way to check consistency for tmpfs
+       ;;
     *)
        _check_generic_filesystem $device
        ;;
@@ -1746,100 +2140,6 @@ _full_platform_details()
      echo "$os/$platform $host $kernel"
 }
 
-_setup_udf_scratchdir()
-{
-    [ "$FSTYP" != "udf" ] \
-       && _fail "setup_udf_testdir: \$FSTYP is not udf"
-    [ -z "$SCRATCH_DEV" -o ! -b "$SCRATCH_DEV" ] \
-       && _notrun "this test requires a valid \$SCRATCH_DEV"
-    [ -z "$SCRATCH_MNT" ] \
-       && _notrun "this test requires a valid \$SCRATCH_MNT"
-
-    # mounted?
-    if _mount | grep -q $SCRATCH_DEV
-    then
-        # if it's mounted, make sure its on $TEST_RW_DIR
-        if ! _mount | grep $SCRATCH_DEV | grep -q $SCRATCH_MNT
-        then
-            _fail "\$SCRATCH_DEV is mounted but not on \$SCRATCH_MNT - aborting"
-        fi
-       $UMOUNT_PROG $SCRATCH_DEV
-    fi
-
-    _scratch_mkfs
-    _scratch_mount
-
-    testdir=$SCRATCH_MNT
-}
-
-_setup_nfs_scratchdir()
-{
-    [ "$FSTYP" != "nfs" ] \
-       && _fail "setup_nfs_testdir: \$FSTYP is not nfs"
-    [ -z "$SCRATCH_DEV" ] \
-       && _notrun "this test requires a valid host fs for \$SCRATCH_DEV"
-    [ -z "$SCRATCH_MNT" ] \
-       && _notrun "this test requires a valid \$SCRATCH_MNT"
-
-    # mounted?
-    if _mount | grep -q $SCRATCH_DEV
-    then
-        # if it's mounted, make sure its on $SCRATCH_MNT
-        if ! _mount | grep $SCRATCH_DEV | grep -q $SCRATCH_MNT
-        then
-            _fail "\$SCRATCH_DEV is mounted but not on \$SCRATCH_MNT - aborting"
-        fi
-       $UMOUNT_PROG $SCRATCH_DEV
-    fi
-
-    _scratch_mkfs
-    _scratch_mount
-
-    testdir=$SCRATCH_MNT
-}
-
-#
-# Warning for UDF and NFS:
-# this function calls _setup_udf_scratchdir and _setup_udf_scratchdir
-# which actually uses the scratch dir for the test dir.
-#
-# This was done because testdir was intended to be a persistent
-# XFS only partition.  This should eventually change, and treat
-# at least local filesystems all the same.
-#
-_setup_testdir()
-{
-    case $FSTYP in
-    udf)
-       _setup_udf_scratchdir
-       ;;
-    nfs*)
-       _setup_nfs_scratchdir
-       ;;
-    *)
-       testdir=$TEST_DIR
-       ;;
-    esac
-}
-
-_cleanup_testdir()
-{
-    case $FSTYP in
-    udf)
-       # umount testdir as it is $SCRATCH_MNT which could be used by xfs next
-       [ -n "$testdir" ] && $UMOUNT_PROG $testdir
-       ;;
-    nfs*)
-       # umount testdir as it is $SCRATCH_MNT which could be used by xfs next
-       [ -n "$testdir" ] && $UMOUNT_PROG $testdir
-       ;;
-    *)
-       # do nothing, testdir is $TEST_DIR
-       :
-       ;;
-    esac
-}
-
 _link_out_file()
 {
        if [ -z "$1" -o -z "$2" ]; then
@@ -1976,16 +2276,24 @@ _test_inode_extsz()
 _require_scratch_dev_pool()
 {
        local i
+       local ndevs
+
        if [ -z "$SCRATCH_DEV_POOL" ]; then
                _notrun "this test requires a valid \$SCRATCH_DEV_POOL"
        fi
 
-       # btrfs test case needs 2 or more scratch_dev_pool; other FS not sure
+       if [ -z "$1" ]; then
+               ndevs=2
+       else
+               ndevs=$1
+       fi
+
+       # btrfs test case needs ndevs or more scratch_dev_pool; other FS not sure
        # so fail it
        case $FSTYP in
        btrfs)
-               if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt 2 ]; then
-                       _notrun "btrfs and this test needs 2 or more disks in SCRATCH_DEV_POOL"
+               if [ "`echo $SCRATCH_DEV_POOL|wc -w`" -lt $ndevs ]; then
+                       _notrun "btrfs and this test needs $ndevs or more disks in SCRATCH_DEV_POOL"
                fi
        ;;
        *)
@@ -1994,10 +2302,10 @@ _require_scratch_dev_pool()
        esac
 
        for i in $SCRATCH_DEV_POOL; do
-               if [ "`_is_block_dev $i`" = "" ]; then
+               if [ "`_is_block_dev "$i"`" = "" ]; then
                        _notrun "this test requires valid block disk $i"
                fi
-               if [ "`_is_block_dev $i`" = "`_is_block_dev $TEST_DEV`" ]; then
+               if [ "`_is_block_dev "$i"`" = "`_is_block_dev "$TEST_DEV"`" ]; then
                        _notrun "$i is part of TEST_DEV, this test requires unique disks"
                fi
                if _mount | grep -q $i; then
@@ -2012,17 +2320,33 @@ _require_scratch_dev_pool()
        done
 }
 
-# We will check if the device is virtual (eg: loop device) since it does not
-# have the delete entry-point. Otherwise SCSI and USB devices are fine. 
+# ensure devices in SCRATCH_DEV_POOL are of the same size
+# must be called after _require_scratch_dev_pool
+_require_scratch_dev_pool_equal_size()
+{
+       local _size
+       local _newsize
+       local _dev
+
+       # SCRATCH_DEV has been set to the first device in SCRATCH_DEV_POOL
+       _size=`_get_device_size $SCRATCH_DEV`
+       for _dev in $SCRATCH_DEV_POOL; do
+               _newsize=`_get_device_size $_dev`
+               if [ $_size -ne $_newsize ]; then
+                       _notrun "This test requires devices in SCRATCH_DEV_POOL have the same size"
+               fi
+       done
+}
+
+# We will check if the device is deletable
 _require_deletable_scratch_dev_pool()
 {
        local i
        local x
        for i in $SCRATCH_DEV_POOL; do
                x=`echo $i | cut -d"/" -f 3`
-               ls -l /sys/class/block/${x} | grep -q "virtual" 
-               if [ $? == "0" ]; then
-                       _notrun "$i is a virtual device which is not deletable"
+               if [ ! -f /sys/class/block/${x}/device/delete ]; then
+                       _notrun "$i is a device which is not deletable"
                fi
        done
 }
@@ -2031,7 +2355,7 @@ _require_deletable_scratch_dev_pool()
 _require_btrfs()
 {
        cmd=$1
-       _require_command $BTRFS_UTIL_PROG btrfs
+       _require_command "$BTRFS_UTIL_PROG" btrfs
        if [ -z "$1" ]; then
                return 1;
        fi
@@ -2044,7 +2368,7 @@ _require_fio()
 {
        job=$1
 
-       _require_command $FIO_PROG
+       _require_command "$FIO_PROG" fio
        if [ -z "$1" ]; then
                return 1;
        fi
@@ -2062,11 +2386,90 @@ _require_freeze()
        [ $result -eq 0 ] || _notrun "$FSTYP does not support freezing"
 }
 
+# Does shutdown work on this fs?
+_require_scratch_shutdown()
+{
+       [ -x src/godown ] || _notrun "src/godown executable not found"
+
+       _scratch_mkfs > /dev/null 2>&1
+       _scratch_mount
+       src/godown -f $SCRATCH_MNT 2>&1 \
+               || _notrun "$FSTYP does not support shutdown"
+       _scratch_unmount
+}
+
+# Does norecovery support by this fs?
+_require_norecovery()
+{
+       _scratch_mount -o ro,norecovery || \
+               _notrun "$FSTYP does not support norecovery"
+       _scratch_unmount
+}
+
+# Does this filesystem support metadata journaling?
+# We exclude ones here that don't; otherwise we assume that it does, so the
+# test will run, fail, and motivate someone to update this test for a new
+# filesystem.
+#
+# It's possible that TEST_DEV and SCRATCH_DEV have different features (it'd be
+# odd, but possible) so check $TEST_DEV by default, but we can optionall pass
+# any dev we want.
+_require_metadata_journaling()
+{
+       if [ -z $1 ]; then
+               DEV=$TEST_DEV
+       else
+               DEV=$1
+       fi
+
+       case "$FSTYP" in
+       ext2|vfat|msdos)
+               _notrun "$FSTYP does not support metadata journaling"
+               ;;
+       ext4)
+               # ext4 could be mkfs'd without a journal...
+               _require_dumpe2fs
+               $DUMPE2FS_PROG -h $DEV 2>&1 | grep -q has_journal || \
+                       _notrun "$FSTYP on $DEV not configured with metadata journaling"
+               ;;
+       *)
+               # by default we pass; if you need to, add your fs above!
+               ;;
+       esac
+}
+
+# Does fiemap support?
+_require_fiemap()
+{
+       _require_xfs_io_command "fiemap"
+}
+
+_count_extents()
+{
+       res=`$XFS_IO_PROG -c "fiemap" $1 | tail -n +2`
+       echo $res | grep -v hole | wc -l | $AWK_PROG '{print $1}'
+}
+
+_count_holes()
+{
+       res=`$XFS_IO_PROG -c "fiemap" $1 | tail -n +2`
+       echo $res | grep hole | wc -l | $AWK_PROG '{print $1}'
+}
+
 # arg 1 is dev to remove and is output of the below eg.
 # ls -l /sys/class/block/sdd | rev | cut -d "/" -f 3 | rev
 _devmgt_remove()
 {
-       echo 1 > /sys/class/scsi_device/${1}/device/delete || _fail "Remove disk failed"
+       local lun=$1
+       local disk=$2
+
+       echo 1 > /sys/class/scsi_device/${lun}/device/delete || _fail "Remove disk failed"
+
+       stat $disk > /dev/null 2>&1
+       while [ $? -eq 0 ]; do
+               sleep 1
+               stat $disk > /dev/null 2>&1
+       done
 }
 
 # arg 1 is dev to add and is output of the below eg.
@@ -2080,6 +2483,31 @@ _devmgt_add()
        tdl=`echo ${1} | cut -d":" -f 2-|sed 's/:/ /g'`
 
        echo ${tdl} >  /sys/class/scsi_host/host${h}/scan || _fail "Add disk failed"
+
+       # ensure the device comes online
+       dev_back_oneline=0
+       for i in `seq 1 10`; do
+               if [ -d /sys/class/scsi_device/${1}/device/block ]; then
+                       dev=`ls /sys/class/scsi_device/${1}/device/block`
+                       for j in `seq 1 10`;
+                       do
+                               stat /dev/$dev > /dev/null 2>&1
+                               if [ $? -eq 0 ]; then
+                                       dev_back_oneline=1
+                                       break
+                               fi
+                               sleep 1
+                       done
+                       break
+               else
+                       sleep 1
+               fi
+       done
+       if [ $dev_back_oneline -eq 0 ]; then
+               echo "/dev/$dev online failed" >> $seqres.full
+       else
+               echo "/dev/$dev is back online" >> $seqres.full
+       fi
 }
 
 _require_fstrim()
@@ -2089,14 +2517,14 @@ _require_fstrim()
        fi
 }
 
-_test_batched_discard()
+_require_batched_discard()
 {
        if [ $# -ne 1 ]; then
-               echo "Usage: _test_batched_discard mnt_point" 1>&2
+               echo "Usage: _require_batched_discard mnt_point" 1>&2
                exit 1
        fi
        _require_fstrim
-       $FSTRIM_PROG ${1} &>/dev/null
+       $FSTRIM_PROG $1 > /dev/null 2>&1 || _notrun "FITRIM not supported on $1"
 }
 
 _require_dumpe2fs()
@@ -2106,6 +2534,71 @@ _require_dumpe2fs()
        fi
 }
 
+_require_ugid_map()
+{
+       if [ ! -e /proc/self/uid_map ]; then
+               _notrun "This test requires procfs uid_map support."
+       fi
+       if [ ! -e /proc/self/gid_map ]; then
+               _notrun "This test requires procfs gid_map support."
+       fi
+}
+
+_require_cp_reflink()
+{
+       cp --help | grep -q reflink || \
+               _notrun "This test requires a cp with --reflink support."
+}
+
+_require_fssum()
+{
+       FSSUM_PROG=$here/src/fssum
+       [ -x $FSSUM_PROG ] || _notrun "fssum not built"
+}
+
+_require_cloner()
+{
+       CLONER_PROG=$here/src/cloner
+       [ -x $CLONER_PROG ] || \
+               _notrun "cloner binary not present at $CLONER_PROG"
+}
+
+# Given 2 files, verify that they have the same mapping but different
+# inodes - i.e. an undisturbed reflink
+# Silent if so, make noise if not
+_verify_reflink()
+{
+       # not a hard link or symlink?
+       cmp -s  <(stat -c '%i' $1) <(stat -c '%i' $2) \
+               && echo "$1 and $2 are not reflinks: same inode number"
+
+       # same mapping?
+       diff -u <($XFS_IO_PROG -c "fiemap" $1 | grep -v $1) \
+               <($XFS_IO_PROG -c "fiemap" $2 | grep -v $2) \
+               || echo "$1 and $2 are not reflinks: different extents"
+}
+
+_require_atime()
+{
+       if [ "$FSTYP" == "nfs" ]; then
+               _notrun "atime related mount options have no effect on NFS"
+       fi
+}
+
+_require_relatime()
+{
+        _scratch_mkfs > /dev/null 2>&1
+        _scratch_mount -o relatime || \
+                _notrun "relatime not supported by the current kernel"
+       _scratch_unmount
+}
+
+_require_userns()
+{
+       [ -x src/nsexec ] || _notrun "src/nsexec executable not found"
+       src/nsexec -U true 2>/dev/null || _notrun "userns not supported by this kernel"
+}
+
 _create_loop_device()
 {
        file=$1
@@ -2133,52 +2626,441 @@ _scale_fsstress_args()
     echo $args
 }
 
+#
+# Return the logical block size if running on a block device,
+# else substitute the page size.
+#
+_min_dio_alignment()
+{
+    dev=$1
+
+    if [ -b "$dev" ]; then
+        blockdev --getss $dev
+    else
+        $here/src/feature -s
+    fi
+}
+
 run_check()
 {
        echo "# $@" >> $seqres.full 2>&1
        "$@" >> $seqres.full 2>&1 || _fail "failed: '$@'"
 }
 
-################################################################################
+_run_btrfs_util_prog()
+{
+       run_check $BTRFS_UTIL_PROG $*
+}
 
-if [ "$iam" != new ]
-then
-    # make some further configuration checks here
+_require_btrfs_send_stream_version()
+{
+       $BTRFS_UTIL_PROG send 2>&1 | \
+               grep '^[ \t]*\-\-stream\-version <version>' > /dev/null 2>&1
+       if [ $? -ne 0 ]; then
+               _notrun "Missing btrfs-progs send --stream-version command line option, skipped this test"
+       fi
 
-    if [ "$TEST_DEV" = ""  ]
-    then
-        echo "common/rc: Error: \$TEST_DEV is not set"
-        exit 1
-    fi
+       # test if btrfs kernel supports send stream version 2
+       if [ ! -f /sys/fs/btrfs/send/stream_version ]; then
+               _notrun "Missing btrfs kernel patch for send stream version 2, skipped this test"
+       fi
+}
 
-    # if $TEST_DEV is not mounted, mount it now as XFS
-    if [ -z "`_fs_type $TEST_DEV`" ]
-    then
-        # $TEST_DEV is not mounted
-        if ! _test_mount
-        then
-            echo "common/rc: retrying test device mount with external set"
-            [ "$USE_EXTERNAL" != "yes" ] && export USE_EXTERNAL=yes
-            if ! _test_mount
-            then
-                echo "common/rc: could not mount $TEST_DEV on $TEST_DIR"
-                exit 1
-            fi
-        fi
-    fi
+_require_btrfs_mkfs_feature()
+{
+       if [ -z $1 ]; then
+               echo "Missing feature name argument for _require_btrfs_mkfs_feature"
+               exit 1
+       fi
+       feat=$1
+       $MKFS_BTRFS_PROG -O list-all 2>&1 | \
+               grep '^[ \t]*'"$feat"'\b' > /dev/null 2>&1
+       [ $? -eq 0 ] || \
+               _notrun "Feature $feat not supported in the available version of mkfs.btrfs"
+}
 
-    if [ "`_fs_type $TEST_DEV`" != "$FSTYP" ]
-    then
-        echo "common/rc: Error: \$TEST_DEV ($TEST_DEV) is not a MOUNTED $FSTYP filesystem"
-        $DF_PROG $TEST_DEV
-        exit 1
-    fi
+_require_btrfs_fs_feature()
+{
+       if [ -z $1 ]; then
+               echo "Missing feature name argument for _require_btrfs_fs_feature"
+               exit 1
+       fi
+       feat=$1
+       modprobe btrfs > /dev/null 2>&1
+       [ -e /sys/fs/btrfs/features/$feat ] || \
+               _notrun "Feature $feat not supported by the available btrfs version"
+}
+
+_require_test_symlinks()
+{
+       # IRIX UDF does not support symlinks
+       [ "$HOSTOS" = "IRIX" -a "$FSTYP" = 'udf' ] && \
+               _notrun "Require symlinks support"
+       target=`mktemp -p $TEST_DIR`
+       link=`mktemp -p $TEST_DIR -u`
+       ln -s `basename $target` $link
+       if [ "$?" -ne 0 ]; then
+               rm -f $target
+               _notrun "Require symlinks support"
+       fi
+       rm -f $target $link
+}
+
+_require_test_fcntl_advisory_locks()
+{
+       [ "$FSTYP" != "cifs" ] && return 0
+       cat /proc/mounts | grep $TEST_DEV | grep cifs | grep -q "nobrl" && return 0
+       cat /proc/mounts | grep $TEST_DEV | grep cifs | grep -qE "nounix|forcemand" && \
+               _notrun "Require fcntl advisory locks support"
+}
+
+_get_total_inode()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_total_inode <mnt>"
+               exit 1
+       fi
+       local nr_inode;
+       nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $3}'`
+       echo $nr_inode
+}
+
+_get_used_inode()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_used_inode <mnt>"
+               exit 1
+       fi
+       local nr_inode;
+       nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $4}'`
+       echo $nr_inode
+}
+
+_get_used_inode_percent()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_used_inode_percent <mnt>"
+               exit 1
+       fi
+       local pct_inode;
+       pct_inode=`$DF_PROG -i $1 | tail -1 | awk '{ print $6 }' | \
+                  sed -e 's/%//'`
+       echo $pct_inode
+}
 
-    # Figure out if we need to add -F ("foreign", deprecated) option to xfs_io
-    xfs_io -c stat $TEST_DIR 2>&1 | grep -q "is not on an XFS filesystem" && \
+_get_free_inode()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_free_inode <mnt>"
+               exit 1
+       fi
+       local nr_inode;
+       nr_inode=`$DF_PROG -i $1 | tail -1 | awk '{print $5}'`
+       echo $nr_inode
+}
+
+# get the available space in bytes
+#
+_get_available_space()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_available_space <mnt>"
+               exit 1
+       fi
+       local avail_kb;
+       avail_kb=`$DF_PROG $1 | tail -n1 | awk '{ print $5 }'`
+       echo $((avail_kb * 1024))
+}
+
+# get btrfs profile configs being tested
+#
+# A set of pre-set profile configs are exported via _btrfs_profile_configs
+# array. Default configs can be overridden by setting BTRFS_PROFILE_CONFIGS
+# var in the format "metadata_profile:data_profile", multiple configs can be
+# seperated by space, e.g.
+# export BTRFS_PROFILE_CONFIGS="raid0:raid0 raid1:raid1 dup:single"
+_btrfs_get_profile_configs()
+{
+       if [ "$FSTYP" != "btrfs" ]; then
+               return
+       fi
+
+       # no user specified btrfs profile configs, export the default configs
+       if [ -z "$BTRFS_PROFILE_CONFIGS" ]; then
+               # default configs
+               _btrfs_profile_configs=(
+                       "-m single -d single"
+                       "-m dup -d single"
+                       "-m raid0 -d raid0"
+                       "-m raid1 -d raid0"
+                       "-m raid1 -d raid1"
+                       "-m raid10 -d raid10"
+                       "-m raid5 -d raid5"
+                       "-m raid6 -d raid6"
+               )
+
+               # remove dup/raid5/raid6 profiles if we're doing device replace
+               # dup profile indicates only one device being used (SCRATCH_DEV),
+               # but we don't want to replace SCRATCH_DEV, which will be used in
+               # _scratch_mount/_check_scratch_fs etc.
+               # and raid5/raid6 doesn't support replace yet
+               if [ "$1" == "replace" ]; then
+                       _btrfs_profile_configs=(
+                               "-m single -d single"
+                               "-m raid0 -d raid0"
+                               "-m raid1 -d raid0"
+                               "-m raid1 -d raid1"
+                               "-m raid10 -d raid10"
+                               # add these back when raid5/6 is working with replace
+                               #"-m raid5 -d raid5"
+                               #"-m raid6 -d raid6"
+                       )
+               fi
+               export _btrfs_profile_configs
+               return
+       fi
+
+       # parse user specified btrfs profile configs
+       local i=0
+       local cfg=""
+       for cfg in $BTRFS_PROFILE_CONFIGS; do
+               # turn "metadata:data" format to "-m metadata -d data"
+               # and assign it to _btrfs_profile_configs array
+               cfg=`echo "$cfg" | sed -e 's/^/-m /' -e 's/:/ -d /'`
+               _btrfs_profile_configs[$i]="$cfg"
+               let i=i+1
+       done
+
+       if [ "$1" == "replace" ]; then
+               if echo ${_btrfs_profile_configs[*]} | grep -q raid[56]; then
+                       _notrun "RAID5/6 doesn't support btrfs device replace yet"
+               fi
+               if echo ${_btrfs_profile_configs[*]} | grep -q dup; then
+                       _notrun "Do not set dup profile in btrfs device replace test"
+               fi
+       fi
+       export _btrfs_profile_configs
+}
+
+# stress btrfs by running balance operation in a loop
+_btrfs_stress_balance()
+{
+       local btrfs_mnt=$1
+       while true; do
+               $BTRFS_UTIL_PROG balance start $btrfs_mnt
+       done
+}
+
+# stress btrfs by creating/mounting/umounting/deleting subvolume in a loop
+_btrfs_stress_subvolume()
+{
+       local btrfs_dev=$1
+       local btrfs_mnt=$2
+       local subvol_name=$3
+       local subvol_mnt=$4
+
+       mkdir -p $subvol_mnt
+       while true; do
+               $BTRFS_UTIL_PROG subvolume create $btrfs_mnt/$subvol_name
+               $MOUNT_PROG -o subvol=$subvol_name $btrfs_dev $subvol_mnt
+               $UMOUNT_PROG $subvol_mnt
+               $BTRFS_UTIL_PROG subvolume delete $btrfs_mnt/$subvol_name
+       done
+}
+
+# stress btrfs by running scrub in a loop
+_btrfs_stress_scrub()
+{
+       local btrfs_mnt=$1
+       while true; do
+               $BTRFS_UTIL_PROG scrub start -B $btrfs_mnt
+       done
+}
+
+# stress btrfs by defragmenting every file/dir in a loop and compress file
+# contents while defragmenting if second argument is not "nocompress"
+_btrfs_stress_defrag()
+{
+       local btrfs_mnt=$1
+       local compress=$2
+
+       while true; do
+               if [ "$compress" == "nocompress" ]; then
+                       find $btrfs_mnt \( -type f -o -type d \) -exec \
+                       $BTRFS_UTIL_PROG filesystem defrag {} \;
+               else
+                       find $btrfs_mnt \( -type f -o -type d \) -exec \
+                       $BTRFS_UTIL_PROG filesystem defrag -clzo {} \;
+                       find $btrfs_mnt \( -type f -o -type d \) -exec \
+                       $BTRFS_UTIL_PROG filesystem defrag -czlib {} \;
+               fi
+       done
+}
+
+# stress btrfs by remounting it with different compression algorithms in a loop
+# run this with fsstress running at background could exercise the compression
+# code path and ensure no race when switching compression algorithm with constant
+# I/O activity.
+_btrfs_stress_remount_compress()
+{
+       local btrfs_mnt=$1
+       while true; do
+               for algo in no zlib lzo; do
+                       $MOUNT_PROG -o remount,compress=$algo $btrfs_mnt
+               done
+       done
+}
+
+# stress btrfs by replacing devices in a loop
+# Note that at least 3 devices are needed in SCRATCH_DEV_POOL and the last
+# device should be free(not used by btrfs)
+_btrfs_stress_replace()
+{
+       local btrfs_mnt=$1
+
+       # The device number in SCRATCH_DEV_POOL should be at least 3,
+       # one is SCRATCH_DEV, one is to be replaced, one is free device
+       # we won't replace SCRATCH_DEV, see below for reason
+       if [ "`echo $SCRATCH_DEV_POOL | wc -w`" -lt 3 ]; then
+               echo "_btrfs_stress_replace requires at least 3 devices in SCRATCH_DEV_POOL"
+               return
+       fi
+
+       # take the last device as the first free_dev
+       local free_dev="`echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $NF}'`"
+
+       # free_dev should be really free
+       if $BTRFS_UTIL_PROG filesystem show $btrfs_mnt | grep -q "$free_dev"; then
+               echo "_btrfs_stress_replace: $free_dev is used by btrfs"
+               return
+       fi
+
+       # dev_pool is device list being currently used by btrfs (excluding SCRATCH_DEV)
+       # and can be replaced. We don't replace SCRATCH_DEV because it will be used in
+       # _scratch_mount and _check_scratch_fs etc.
+       local dev_pool=`echo $SCRATCH_DEV_POOL | sed -e "s# *$SCRATCH_DEV *##" \
+                       -e "s# *$free_dev *##"`
+
+       # set the first device in dev_pool as the first src_dev to be replaced
+       local src_dev=`echo $dev_pool | $AWK_PROG '{print $1}'`
+
+       echo "dev_pool=$dev_pool"
+       echo "free_dev=$free_dev, src_dev=$src_dev"
+       while true; do
+               echo "Replacing $src_dev with $free_dev"
+               $BTRFS_UTIL_PROG replace start -fB $src_dev $free_dev $btrfs_mnt
+               if [ $? -ne 0 ]; then
+                       # don't update src_dev and free_dev if replace failed
+                       continue
+               fi
+               dev_pool="$dev_pool $free_dev"
+               dev_pool=`echo $dev_pool | sed -e "s# *$src_dev *##"`
+               free_dev=$src_dev
+               src_dev=`echo $dev_pool | $AWK_PROG '{print $1}'`
+       done
+}
+
+# find the right option to force output in bytes, older versions of btrfs-progs
+# print that by default, newer print human readable numbers with unit suffix
+_btrfs_qgroup_units()
+{
+       $BTRFS_UTIL_PROG qgroup show --help 2>&1 | grep -q -- --raw && echo "--raw"
+}
+
+# return device size in kb
+_get_device_size()
+{
+       grep `_short_dev $1` /proc/partitions | awk '{print $3}'
+}
+
+# don't check dmesg log after test
+_disable_dmesg_check()
+{
+       rm -f ${RESULT_DIR}/check_dmesg
+}
+
+init_rc()
+{
+       if [ "$iam" == new ]
+       then
+               return
+       fi
+       # make some further configuration checks here
+       if [ "$TEST_DEV" = ""  ]
+       then
+               echo "common/rc: Error: \$TEST_DEV is not set"
+               exit 1
+       fi
+
+       # if $TEST_DEV is not mounted, mount it now as XFS
+       if [ -z "`_fs_type $TEST_DEV`" ]
+       then
+               # $TEST_DEV is not mounted
+               if ! _test_mount
+               then
+                       echo "common/rc: retrying test device mount with external set"
+                       [ "$USE_EXTERNAL" != "yes" ] && export USE_EXTERNAL=yes
+                       if ! _test_mount
+                       then
+                               echo "common/rc: could not mount $TEST_DEV on $TEST_DIR"
+                               exit 1
+                       fi
+               fi
+       fi
+
+       if [ "`_fs_type $TEST_DEV`" != "$FSTYP" ]
+       then
+               echo "common/rc: Error: \$TEST_DEV ($TEST_DEV) is not a MOUNTED $FSTYP filesystem"
+               $DF_PROG $TEST_DEV
+               exit 1
+       fi
+       # Figure out if we need to add -F ("foreign", deprecated) option to xfs_io
+       xfs_io -c stat $TEST_DIR 2>&1 | grep -q "is not on an XFS filesystem" && \
        export XFS_IO_PROG="$XFS_IO_PROG -F"
 
-fi
+       # xfs_copy doesn't work on v5 xfs yet without -d option
+       if [ "$FSTYP" == "xfs" ] && [[ $MKFS_OPTIONS =~ crc=1 ]]; then
+               export XFS_COPY_PROG="$XFS_COPY_PROG -d"
+       fi
+}
 
+# get real device path name by following link
+_real_dev()
+{
+       local _dev=$1
+       if [ -b "$_dev" ] && [ -L "$_dev" ]; then
+               _dev=`readlink -f "$_dev"`
+       fi
+       echo $_dev
+}
+
+# basename of a device
+_short_dev()
+{
+       echo `basename $(_real_dev $1)`
+}
+
+_sysfs_dev()
+{
+       local _dev=$1
+       local _maj=$(stat -c%t $_dev | tr [:lower:] [:upper:])
+       local _min=$(stat -c%T $_dev | tr [:lower:] [:upper:])
+       _maj=$(echo "ibase=16; $_maj" | bc)
+       _min=$(echo "ibase=16; $_min" | bc)
+       echo /sys/dev/block/$_maj:$_min
+}
+
+get_block_size()
+{
+       if [ -z $1 ] || [ ! -d $1 ]; then
+               echo "Missing mount point argument for get_block_size"
+               exit 1
+       fi
+       echo `stat -f -c %S $1`
+}
+
+init_rc
+
+################################################################################
 # make sure this script returns success
 /bin/true