common/rc: Add extra check for xfs_io -c "chattr" on XFS
[xfstests-dev.git] / common / rc
index 0306e93cb1caaf6ba5abf92d0f765a62f1c0786d..807a1f6ca225c5b5fbbde1af7a62f4e1f55ff929 100644 (file)
--- a/common/rc
+++ b/common/rc
@@ -170,6 +170,16 @@ _get_filesize()
     stat -c %s "$1"
 }
 
+# Get hugepagesize in bytes
+_get_hugepagesize()
+{
+       local hugepgsz=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
+       # Call _notrun if $hugepgsz is not a number
+       echo "$hugepgsz" | egrep -q ^[0-9]+$ || \
+               _notrun "Cannot get the value of Hugepagesize"
+       echo $((hugepgsz * 1024))
+}
+
 _mount()
 {
     $MOUNT_PROG `_mount_ops_filter $*`
@@ -571,7 +581,7 @@ _setup_large_ext4_fs()
 _scratch_mkfs_ext4()
 {
        local mkfs_cmd="$MKFS_EXT4_PROG -F"
-       local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \""
+       local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
        local tmp=`mktemp -u`
        local mkfs_status
 
@@ -1051,7 +1061,19 @@ _scratch_mkfs_geom()
 
     case $FSTYP in
     xfs)
-       MKFS_OPTIONS+=" -b size=$blocksize, -d su=$sunit_bytes,sw=$swidth_mult"
+       if echo "$MKFS_OPTIONS" | egrep -q "b?size="; then
+               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+/\1$blocksize/")
+       else
+               MKFS_OPTIONS+=" -b size=$blocksize"
+       fi
+
+       if echo "$MKFS_OPTIONS" | egrep -q "(su|sunit|sw|swidth)="; then
+               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r \
+                       -e "s/(su|sunit)=[0-9kmg]+/su=$sunit_bytes/" \
+                       -e "s/(sw|swidth)=[0-9kmg]+/sw=$swidth_mult/")
+       else
+               MKFS_OPTIONS+=" -d su=$sunit_bytes,sw=$swidth_mult"
+       fi
        ;;
     ext4|ext4dev)
        MKFS_OPTIONS+=" -b $blocksize -E stride=$sunit_blocks,stripe_width=$swidth_blocks"
@@ -1874,6 +1896,17 @@ _require_dm_target()
        _require_sane_bdev_flush $SCRATCH_DEV
        _require_command "$DMSETUP_PROG" dmsetup
 
+       _normalize_mount_options | egrep -q "dax(=always| |$)"
+       if [ $? -eq 0 ]; then
+               case $target in
+               stripe|linear|log-writes)
+                       ;;
+               *)
+                       _notrun "Cannot run tests with DAX on $target devices."
+                       ;;
+               esac
+       fi
+
        modprobe dm-$target >/dev/null 2>&1
 
        $DMSETUP_PROG targets 2>&1 | grep -q ^$target
@@ -2125,19 +2158,31 @@ _require_xfs_io_command()
        local param="$*"
        local param_checked=""
        local opts=""
+       local attr_info=""
 
        local testfile=$TEST_DIR/$$.xfs_io
        local testio
        case $command in
        "chattr")
+               local testdir=$TEST_DIR/$$.attr_dir
+               mkdir $TEST_DIR/$$.attr_dir
                if [ -z "$param" ]; then
                        param=s
                fi
                # Test xfs_io chattr support AND
                # filesystem FS_IOC_FSSETXATTR support
-               testio=`$XFS_IO_PROG -F -f -c "chattr +$param" $testfile 2>&1`
-               $XFS_IO_PROG -F -f -r -c "chattr -$param" $testfile 2>&1
+               # 'tPnE' flags are only valid for a directory so check them on a directory.
+               if echo "$param" | egrep -q 't|P|n|E'; then
+                       testio=`$XFS_IO_PROG -F -c "chattr +$param" $testdir 2>&1`
+                       attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testdir | awk '{print $1}'`
+                       $XFS_IO_PROG -F -r -c "chattr -$param" $testdir 2>&1
+               else
+                       testio=`$XFS_IO_PROG -F -f -c "chattr +$param" $testfile 2>&1`
+                       attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testfile | awk '{print $1}'`
+                       $XFS_IO_PROG -F -r -c "chattr -$param" $testfile 2>&1
+               fi
                param_checked="+$param"
+               rm -rf $testdir 2>&1 > /dev/null
                ;;
        "chproj")
                testio=`$XFS_IO_PROG -F -f -c "chproj 0" $testfile 2>&1`
@@ -2244,6 +2289,8 @@ _require_xfs_io_command()
                _notrun "xfs_io $command $param_checked not supported on $FSTYP"
        echo $testio | grep -q "Function not implemented" && \
                _notrun "xfs_io $command $param_checked support is missing (missing syscall?)"
+       echo $testio | grep -q "unknown flag" && \
+               _notrun "xfs_io $command $param_checked support is missing (unknown flag)"
 
        [ -n "$param" ] || return
 
@@ -2256,6 +2303,19 @@ _require_xfs_io_command()
                echo $testio | grep -q "\(invalid option\|not supported\)" && \
                        _notrun "xfs_io $command doesn't support $param"
        fi
+
+       # On XFS, ioctl(FSSETXATTR)(called by xfs_io -c "chattr") maskes off unsupported
+       # or invalid flags silently so need to check these flags by extra ioctl(FSGETXATTR)
+       # (called by xfs_io -c "lsattr").
+       # The following URL explains why we don't change the behavior of XFS.
+       # https://www.spinics.net/lists/linux-xfs/msg44725.html
+       if [ -n "$attr_info" -a "$FSTYP" = "xfs" ]; then
+               local num=${#param}
+               for i in $(seq 0 $((num-1))); do
+                       echo $attr_info | grep -q "${param:$i:1}" || \
+                               _notrun "xfs_io $command +${param:$i:1} support is missing (unknown flag in kernel)"
+               done
+       fi
 }
 
 # check that kernel and filesystem support direct I/O
@@ -2298,7 +2358,7 @@ _require_scratch_swapfile()
        _require_scratch
        _require_command "$MKSWAP_PROG" "mkswap"
 
-       _scratch_mkfs >/dev/null
+       _scratch_mkfs >/dev/null 2>&1
 
        # With mounting SELinux context(e.g. system_u:object_r:root_t:s0),
        # standard mkswap tried to reset the type of default context to
@@ -2346,7 +2406,7 @@ _require_fs_space()
 _require_sparse_files()
 {
     case $FSTYP in
-    hfsplus)
+    hfsplus|exfat)
         _notrun "Sparse files not supported by this filesystem type: $FSTYP"
        ;;
     *)
@@ -3161,18 +3221,81 @@ _require_scratch_shutdown()
        _scratch_unmount
 }
 
-# Does dax mount option work on this dev/fs?
-_require_scratch_dax()
+_check_s_dax()
 {
+       local target=$1
+       local exp_s_dax=$2
+
+       local attributes=$($XFS_IO_PROG -c 'statx -r' $target | awk '/stat.attributes / { print $3 }')
+       if [ $exp_s_dax -eq 0 ]; then
+               (( attributes & 0x2000 )) && echo "$target has unexpected S_DAX flag"
+       else
+               (( attributes & 0x2000 )) || echo "$target doen't have expected S_DAX flag"
+       fi
+}
+
+_check_xflag()
+{
+       local target=$1
+       local exp_xflag=$2
+
+       if [ $exp_xflag -eq 0 ]; then
+               _test_inode_flag dax $target && echo "$target has unexpected FS_XFLAG_DAX flag"
+       else
+               _test_inode_flag dax $target || echo "$target doen't have expected FS_XFLAG_DAX flag"
+       fi
+}
+
+# Check if dax mount options are supported
+#
+# $1 can be either 'dax=always' or 'dax'
+#
+# dax=always
+#      Check for the new dax options (dax=inode, dax=always or dax=never)
+#      by passing "dax=always".
+# dax
+#      Check for the old dax or new dax=always by passing "dax".
+#
+# This only accepts 'dax=always' because dax=always, dax=inode and
+# dax=never are always supported together.  So if the other options are
+# required checking for 'dax=always' indicates support for the other 2.
+#
+# Return 0 if filesystem/device supports the specified dax option.
+# Return 1 if mount fails with the specified dax option.
+# Return 2 if /proc/mounts shows wrong dax option.
+_check_scratch_dax_mountopt()
+{
+       local option=$1
+
        _require_scratch
        _scratch_mkfs > /dev/null 2>&1
-       _try_scratch_mount -o dax || \
-               _notrun "mount $SCRATCH_DEV with dax failed"
-       # Check options to be sure. XFS ignores dax option
-       # and goes on if dev underneath does not support dax.
-       _fs_options $SCRATCH_DEV | grep -qw "dax" || \
-               _notrun "$SCRATCH_DEV $FSTYP does not support -o dax"
-       _scratch_unmount
+
+       _try_scratch_mount "-o $option" > /dev/null 2>&1 || return 1
+
+       if _fs_options $SCRATCH_DEV | egrep -q "dax(=always|,|$)"; then
+               _scratch_unmount
+               return 0
+       else
+               _scratch_unmount
+               return 2
+       fi
+}
+
+# Throw notrun if _check_scratch_dax_mountopt() returns a non-zero value.
+_require_scratch_dax_mountopt()
+{
+       local mountopt=$1
+
+       _check_scratch_dax_mountopt "$mountopt"
+       local res=$?
+
+       [ $res -eq 1 ] && _notrun "mount $SCRATCH_DEV with $mountopt failed"
+       [ $res -eq 2 ] && _notrun "$SCRATCH_DEV $FSTYP does not support -o $mountopt"
+}
+
+_require_dax_iflag()
+{
+       _require_xfs_io_command "chattr" "x"
 }
 
 # Does norecovery support by this fs?
@@ -3200,7 +3323,7 @@ _has_metadata_journaling()
        fi
 
        case "$FSTYP" in
-       ext2|vfat|msdos|udf)
+       ext2|vfat|msdos|udf|exfat)
                echo "$FSTYP does not support metadata journaling"
                return 1
                ;;
@@ -3385,12 +3508,16 @@ _normalize_mount_options()
 }
 
 # skip test if MOUNT_OPTIONS contains the given strings
+# Both dax and dax=always are excluded if dax or dax=always is passed
 _exclude_scratch_mount_option()
 {
        local mnt_opts=$(_normalize_mount_options)
 
        while [ $# -gt 0 ]; do
-               if echo $mnt_opts | grep -qw "$1"; then
+               local pattern=$1
+               echo "$pattern" | egrep -q "dax(=always|$)" && \
+                       pattern="dax(=always| |$)"
+               if echo $mnt_opts | egrep -q "$pattern"; then
                        _notrun "mount option \"$1\" not allowed in this test"
                fi
                shift
@@ -3470,7 +3597,7 @@ run_check()
        "$@" >> $seqres.full 2>&1 || _fail "failed: '$@'"
 }
 
-_require_test_symlinks()
+_require_symlinks()
 {
        local target=`mktemp -p $TEST_DIR`
        local link=`mktemp -p $TEST_DIR -u`
@@ -3482,6 +3609,18 @@ _require_test_symlinks()
        rm -f $target $link
 }
 
+_require_hardlinks()
+{
+       local target=`mktemp -p $TEST_DIR`
+       local link=`mktemp -p $TEST_DIR -u`
+       ln $target $link
+       if [ "$?" -ne 0 ]; then
+               rm -f $target
+               _notrun "No hardlink support"
+       fi
+       rm -f $target $link
+}
+
 _require_test_fcntl_advisory_locks()
 {
        [ "$FSTYP" != "cifs" ] && return 0
@@ -3490,6 +3629,14 @@ _require_test_fcntl_advisory_locks()
                _notrun "Require fcntl advisory locks support"
 }
 
+_require_test_fcntl_setlease()
+{
+       _require_test_program "locktest"
+       touch $TEST_DIR/setlease_testfile
+       $here/src/locktest -t $TEST_DIR/setlease_testfile >/dev/null 2>&1
+       [ $? -eq 22 ] && _notrun "Require fcntl setlease support"
+}
+
 _require_ofd_locks()
 {
        # Give a test run by getlk wrlck on testfile.
@@ -4157,6 +4304,40 @@ _check_xfs_scrub_does_unicode() {
        return 0
 }
 
+# exfat timestamps start at 1980 and cannot be prior to epoch
+_require_negative_timestamps() {
+       case "$FSTYP" in
+       ceph|exfat)
+               _notrun "$FSTYP does not support negative timestamps"
+               ;;
+       esac
+}
+
+# Require the 'accton' userspace tool and CONFIG_BSD_PROCESS_ACCT=y.
+_require_bsd_process_accounting()
+{
+       _require_command "$ACCTON_PROG" accton
+       $ACCTON_PROG on &> $tmp.test_accton
+       cat $tmp.test_accton >> $seqres.full
+       if grep 'Function not implemented' $tmp.test_accton; then
+               _notrun "BSD process accounting support unavailable"
+       fi
+       $ACCTON_PROG off >> $seqres.full
+}
+
+_require_sysctl_variable()
+{
+       local name=$1
+       sysctl $name &>/dev/null || _notrun "$name sysctl unavailable"
+}
+
+_require_mknod()
+{
+       mknod $TEST_DIR/$seq.null c 1 3 \
+               || _notrun "$FSTYP does not support mknod/mkfifo"
+       rm -f $TEST_DIR/$seq.null
+}
+
 init_rc
 
 ################################################################################