common/rc: fix unicode checker detection in xfs_scrub
[xfstests-dev.git] / common / rc
index 27a27ea36f75336556250e53ad1c52072b71273c..b3289de985d80f6c8d40f533c00ae76d6ce90416 100644 (file)
--- a/common/rc
+++ b/common/rc
@@ -4,17 +4,7 @@
 
 . common/config
 
-BC=$(which bc 2> /dev/null) || BC=
-
-# Some tests are not relevant or functional when testing XFS realtime
-# subvolumes along with the rtinherit=1 mkfs option.  In these cases,
-# this test will opt-out of the test.
-_require_no_rtinherit()
-{
-       [ "$FSTYP" = "xfs" ] && echo "$MKFS_OPTIONS" |
-               egrep -q "rtinherit([^=]|=1|$)" && \
-               _notrun "rtinherit mkfs option is not supported by this test."
-}
+BC="$(type -P bc)" || BC=
 
 _require_math()
 {
@@ -138,6 +128,7 @@ case "$FSTYP" in
     9p)
         ;;
     ceph)
+        . ./common/ceph
         ;;
     glusterfs)
         ;;
@@ -286,12 +277,11 @@ _mount_ops_filter()
     local params="$*"
     local last_index=$(( $# - 1 ))
 
-    #get mount point to handle dmapi mtpt option correctly
     [ $last_index -gt 0 ] && shift $last_index
     local fs_escaped=$1
 
-    echo $params | sed -e 's/dmapi/dmi/' \
-        $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$fs_escaped\1\2#; print;"
+    echo $params | \
+        $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$fs_escaped\1\2#; print;"
 
 }
 
@@ -349,7 +339,37 @@ _try_scratch_mount()
 # mount scratch device with given options and _fail if mount fails
 _scratch_mount()
 {
-       _try_scratch_mount $* || _fail "mount failed"
+       _try_scratch_mount $* || _fail "mount $(_scratch_mount_options $*) failed"
+}
+
+_scratch_mount_idmapped()
+{
+       local type="$1"
+       local id="$2"
+
+       if [ "$type" = "u" ]; then
+               # This means root will be able to create files as uid %id in
+               # the underlying filesystem by going through the idmapped mount.
+               $here/src/idmapped-mounts/mount-idmapped --map-mount u:0:$id:1 \
+                                                        --map-mount u:$id:0:1 \
+                                                        --map-mount g:0:0:1 \
+                                                        "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+       elif [ "$type" = "g" ]; then
+               # This means root will be able to create files as gid %id in
+               # the underlying filesystem by going through the idmapped mount.
+               $here/src/idmapped-mounts/mount-idmapped --map-mount g:0:$id:1 \
+                                                        --map-mount g:$id:0:1 \
+                                                        --map-mount u:0:0:1 \
+                                                        "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+       elif [ "$type" = "b" ]; then
+               # This means root will be able to create files as uid and gid
+               # %id in the underlying filesystem by going through the idmapped mount.
+               $here/src/idmapped-mounts/mount-idmapped --map-mount b:0:$id:1 \
+                                                        --map-mount b:$id:0:1 \
+                                                        "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+       else
+               _fail "usage: either \"u\" (uid), \"g\" (gid), or \"b\" (uid and gid) must be specified "
+       fi
 }
 
 _scratch_unmount()
@@ -367,6 +387,11 @@ _scratch_unmount()
        esac
 }
 
+_scratch_umount_idmapped()
+{
+       $UMOUNT_PROG $SCRATCH_MNT
+}
+
 _scratch_remount()
 {
     local opts="$1"
@@ -499,18 +524,6 @@ _scratch_do_mkfs()
        return $mkfs_status
 }
 
-_scratch_metadump()
-{
-       local dumpfile=$1
-       shift
-       local options=
-
-       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
-               options="-l $SCRATCH_LOGDEV"
-
-       xfs_metadump $options "$@" $SCRATCH_DEV $dumpfile
-}
-
 _setup_large_ext4_fs()
 {
        local fs_size=$1
@@ -606,6 +619,42 @@ _scratch_mkfs_ext4()
        return $mkfs_status
 }
 
+_ext4_metadump()
+{
+       local device="$1"
+       local dumpfile="$2"
+       local compressopt="$3"
+
+       test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
+       $E2IMAGE_PROG -Q "$device" "$dumpfile"
+       [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
+               $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
+}
+
+# Capture the metadata of a filesystem in a dump file for offline analysis.
+# This is not supported by all filesystem types, so this function should only
+# be used after a test has already failed.
+_metadump_dev() {
+       local device="$1"
+       local dumpfile="$2"
+       local compressopt="$3"
+
+       test "$DUMP_CORRUPT_FS" = 1 || return 0
+
+       case "$FSTYP" in
+       ext*)
+               _ext4_metadump $device $dumpfile $compressopt
+               ;;
+       xfs)
+               _xfs_metadump $dumpfile $device none $compressopt
+               ;;
+       *)
+               echo "Don't know how to metadump $FSTYP"
+               return 1
+               ;;
+       esac
+}
+
 _test_mkfs()
 {
     case $FSTYP in
@@ -868,15 +917,15 @@ _scratch_dev_pool_get()
                _fail "Usage: _scratch_dev_pool_get ndevs"
        fi
 
-       local test_ndevs=$1
-       local config_ndevs=`echo $SCRATCH_DEV_POOL| wc -w`
-       local -a devs="( $SCRATCH_DEV_POOL )"
-
-       typeset -p config_ndevs >/dev/null 2>&1
+       typeset -p SCRATCH_DEV_POOL >/dev/null 2>&1
        if [ $? -ne 0 ]; then
                _fail "Bug: cant find SCRATCH_DEV_POOL ndevs"
        fi
 
+       local test_ndevs=$1
+       local config_ndevs=`echo $SCRATCH_DEV_POOL| wc -w`
+       local -a devs="( $SCRATCH_DEV_POOL )"
+
        if [ $config_ndevs -lt $test_ndevs ]; then
                _notrun "Need at least test requested number of ndevs $test_ndevs"
        fi
@@ -931,6 +980,16 @@ _available_memory_bytes()
        fi
 }
 
+_check_minimal_fs_size()
+{
+       local fssize=$1
+
+       if [ -n "$MIN_FSSIZE" ]; then
+               [ $MIN_FSSIZE -gt "$fssize" ] &&
+                       _notrun "specified filesystem size is too small"
+       fi
+}
+
 # Create fs of certain size on scratch device
 # _scratch_mkfs_sized <size in bytes> [optional blocksize]
 _scratch_mkfs_sized()
@@ -943,7 +1002,10 @@ _scratch_mkfs_sized()
        xfs)
                def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?size= ?+([0-9]+).*/\1/p'`
                ;;
-       ext2|ext3|ext4|ext4dev|udf|btrfs|reiser4|ocfs2|reiserfs)
+       btrfs)
+               def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-s ?+([0-9]+).*/\1/p'`
+               ;;
+       ext2|ext3|ext4|ext4dev|udf|reiser4|ocfs2|reiserfs)
                def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?+([0-9]+).*/\1/p'`
                ;;
        jfs)
@@ -964,6 +1026,8 @@ _scratch_mkfs_sized()
 
        local blocks=`expr $fssize / $blocksize`
 
+       _check_minimal_fs_size $fssize
+
        if [ -b "$SCRATCH_DEV" ]; then
                local devsize=`blockdev --getsize64 $SCRATCH_DEV`
                [ "$fssize" -gt "$devsize" ] && _notrun "Scratch device too small"
@@ -986,7 +1050,7 @@ _scratch_mkfs_sized()
                fi
                ;;
        ext2|ext3|ext4|ext4dev)
-               ${MKFS_PROG}.$FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
+               ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
                ;;
        gfs2)
                # mkfs.gfs2 doesn't automatically shrink journal files on small
@@ -1001,10 +1065,10 @@ _scratch_mkfs_sized()
                        (( journal_size >= min_journal_size )) || journal_size=$min_journal_size
                        MKFS_OPTIONS="-J $journal_size $MKFS_OPTIONS"
                fi
-               ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV $blocks
+               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV $blocks
                ;;
        ocfs2)
-               yes | ${MKFS_PROG}.$FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
+               yes | ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
                ;;
        udf)
                $MKFS_UDF_PROG $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
@@ -1018,10 +1082,10 @@ _scratch_mkfs_sized()
                $MKFS_BTRFS_PROG $MKFS_OPTIONS $mixed_opt -b $fssize $SCRATCH_DEV
                ;;
        jfs)
-               ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS $SCRATCH_DEV $blocks
+               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS $SCRATCH_DEV $blocks
                ;;
        reiserfs)
-               ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
+               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
                ;;
        reiser4)
                # mkfs.resier4 requires size in KB as input for creating filesystem
@@ -1040,6 +1104,9 @@ _scratch_mkfs_sized()
                fi
                export MOUNT_OPTIONS="-o size=$fssize $TMPFS_MOUNT_OPTIONS"
                ;;
+       bcachefs)
+               $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS --fs_size=$fssize --block_size=$blocksize $SCRATCH_DEV
+               ;;
        *)
                _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
                ;;
@@ -1061,7 +1128,7 @@ _scratch_mkfs_geom()
     case $FSTYP in
     xfs)
        if echo "$MKFS_OPTIONS" | egrep -q "b?size="; then
-               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+/\1$blocksize/")
+               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+k?/\1$blocksize/")
        else
                MKFS_OPTIONS+=" -b size=$blocksize"
        fi
@@ -1088,30 +1155,44 @@ _scratch_mkfs_geom()
 # _scratch_mkfs_blocksized blocksize
 _scratch_mkfs_blocksized()
 {
-    local blocksize=$1
+       local blocksize=$1
 
-    local re='^[0-9]+$'
-    if ! [[ $blocksize =~ $re ]] ; then
-        _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
-    fi
+       local re='^[0-9]+$'
+       if ! [[ $blocksize =~ $re ]] ; then
+               _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
+       fi
 
-    case $FSTYP in
-    xfs)
-       _scratch_mkfs_xfs $MKFS_OPTIONS -b size=$blocksize
-       ;;
-    ext2|ext3|ext4)
-       ${MKFS_PROG}.$FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV
-       ;;
-    gfs2)
-       ${MKFS_PROG}.$FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV
-       ;;
-    ocfs2)
-       yes | ${MKFS_PROG}.$FSTYP -F $MKFS_OPTIONS -b $blocksize -C $blocksize $SCRATCH_DEV
-       ;;
-    *)
-       _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_blocksized"
-       ;;
-    esac
+       case $FSTYP in
+       btrfs)
+               test -f /sys/fs/btrfs/features/supported_sectorsizes || \
+               _notrun "Subpage sectorsize support is not found in $FSTYP"
+
+               grep -wq $blocksize /sys/fs/btrfs/features/supported_sectorsizes || \
+               _notrun "$FSTYP does not support sectorsize=$blocksize yet"
+
+               _scratch_mkfs --sectorsize=$blocksize
+               ;;
+       xfs)
+               _scratch_mkfs_xfs $MKFS_OPTIONS -b size=$blocksize
+               ;;
+       ext2|ext3|ext4)
+               ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV
+               ;;
+       gfs2)
+               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV
+               ;;
+       ocfs2)
+               yes | ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize \
+                                               -C $blocksize $SCRATCH_DEV
+               ;;
+       bcachefs)
+               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS --block_size=$blocksize \
+                                                               $SCRATCH_DEV
+               ;;
+       *)
+               _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_blocksized"
+               ;;
+       esac
 }
 
 _scratch_resvblks()
@@ -1154,6 +1235,11 @@ _repair_scratch_fs()
        fi
        return $res
         ;;
+    bcachefs)
+       # With bcachefs, if fsck detects any errors we consider it a bug and we
+       # want the test to fail:
+       _check_scratch_fs
+       ;;
     *)
        local dev=$SCRATCH_DEV
        local fstyp=$FSTYP
@@ -1506,7 +1592,7 @@ _require_scratch_nocheck()
                        _notrun "this test requires a valid \$SCRATCH_MNT"
                fi
                ;;
-       nfs*|ceph)
+       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"
@@ -1515,6 +1601,15 @@ _require_scratch_nocheck()
                        _notrun "this test requires a valid \$SCRATCH_MNT"
                fi
                ;;
+       ceph)
+               echo $SCRATCH_DEV | grep -qE "=/|:/" > /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
+               ;;
        pvfs2)
                echo $SCRATCH_DEV | grep -q "://" > /dev/null 2>&1
                if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
@@ -1547,7 +1642,7 @@ _require_scratch_nocheck()
        tmpfs)
                if [ -z "$SCRATCH_DEV" -o ! -d "$SCRATCH_MNT" ];
                then
-                   _notrun "this test requires a valid \$SCRATCH_MNT and unique $SCRATCH_DEV"
+                   _notrun "this test requires a valid \$SCRATCH_MNT and unique \$SCRATCH_DEV"
                fi
                ;;
        ubifs)
@@ -1590,6 +1685,28 @@ _require_scratch_nocheck()
     rm -f ${RESULT_DIR}/require_scratch
 }
 
+# we need the scratch device and it needs to not be an lvm device
+_require_scratch_nolvm()
+{
+       _require_scratch_nocheck
+
+       # This works if we don't have LVM, all we want is to skip if the scratch
+       # device is an lvm device.
+       $LVM_PROG lvdisplay $SCRATCH_DEV > /dev/null 2>&1
+       [ $? -eq 0 ] && _notrun "test requires a non-lvm scratch device"
+}
+
+_require_no_compress()
+{
+       case "$FSTYP" in
+       btrfs)
+               _require_btrfs_no_compress
+               ;;
+       *)
+               ;;
+       esac
+}
+
 # we need the scratch device and it should be checked post test.
 _require_scratch()
 {
@@ -1607,6 +1724,51 @@ _require_scratch_size()
        [ $devsize -lt $1 ] && _notrun "scratch dev too small"
 }
 
+# require a scratch dev of a minimum size (in kb) and should not be checked
+# post test
+_require_scratch_size_nocheck()
+{
+       [ $# -eq 1 ] || _fail "_require_scratch_size: expected size param"
+
+       _require_scratch_nocheck
+       local devsize=`_get_device_size $SCRATCH_DEV`
+       [ $devsize -lt $1 ] && _notrun "scratch dev too small"
+}
+
+# Require scratch fs which supports >16T of filesystem size.
+# _require_scratch must be called before this function is called.
+_require_scratch_16T_support()
+{
+       case $FSTYP in
+       ext2|ext3|f2fs)
+               _notrun "$FSTYP doesn't support >16T filesystem"
+               ;;
+       ext4)
+               _scratch_mkfs >> $seqres.full 2>&1
+               _scratch_mount
+               local blocksize=$(_get_block_size $SCRATCH_MNT)
+               if [ $blocksize -lt 4096 ]; then
+                       _notrun "This test requires >16T fs support"
+               fi
+               _scratch_unmount
+               ;;
+       *)
+               ;;
+       esac
+}
+
+# Require scratch fs supports delay allocation.
+_require_scratch_delalloc()
+{
+       _require_command "$FILEFRAG_PROG" filefrag
+
+       _scratch_mkfs > $seqres.full
+       _scratch_mount
+       $XFS_IO_PROG -f -c 'pwrite 0 64k' $SCRATCH_MNT/testy &> /dev/null
+       $FILEFRAG_PROG -v $SCRATCH_MNT/testy 2>&1 | grep -q delalloc || \
+               _notrun "test requires delayed allocation buffered writes"
+       _scratch_unmount
+}
 
 # this test needs a test partition - check we're ok & mount it
 #
@@ -1630,7 +1792,7 @@ _require_test()
                        _notrun "this test requires a valid \$TEST_DIR"
                fi
                ;;
-       nfs*|ceph)
+       nfs*)
                echo $TEST_DEV | grep -q ":/" > /dev/null 2>&1
                if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
                        _notrun "this test requires a valid \$TEST_DEV"
@@ -1639,6 +1801,15 @@ _require_test()
                        _notrun "this test requires a valid \$TEST_DIR"
                fi
                ;;
+       ceph)
+               echo $TEST_DEV | grep -qE "=/|:/" > /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
+               ;;
        cifs)
                echo $TEST_DEV | grep -q "//" > /dev/null 2>&1
                if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
@@ -1743,28 +1914,18 @@ _require_loop()
     else
        _notrun "This test requires loopback device support"
     fi
-}
 
-# this test requires ext2 filesystem support
-#
-_require_ext2()
-{
-    modprobe ext2 >/dev/null 2>&1
-    if grep ext2 /proc/filesystems >/dev/null 2>&1
-    then
-       :
-    else
-       _notrun "This test requires ext2 filesystem support"
-    fi
+    # loop device does not handle zone information
+    _require_non_zoned_device ${TEST_DEV}
 }
 
-# this test requires tmpfs filesystem support
+# this test requires kernel support for a secondary filesystem
 #
-_require_tmpfs()
+_require_extra_fs()
 {
-       modprobe tmpfs >/dev/null 2>&1
-       grep -q tmpfs /proc/filesystems ||
-               _notrun "this test requires tmpfs support"
+       modprobe "$1" >/dev/null 2>&1
+       grep -q -w "$1" /proc/filesystems ||
+               _notrun "this test requires $1 support"
 }
 
 # this test requires that (large) loopback device files are not in use
@@ -1856,10 +2017,33 @@ _require_sane_bdev_flush()
        fi
 }
 
+# Decide if the scratch filesystem is likely to be mounted in fsdax mode.
+# It goes 3 ways based on mount options::
+#      1. "dax" or "dax=always" means always test using DAX
+#      2. "dax=never" means we'll never use DAX
+#      3. "dax=inode" or nothing means "use scratch dev capability" to
+#          determine whether DAX is going to be used.
+#
+# Returns 0 if DAX will be used, 1 if DAX is not going to be used.
+__scratch_uses_fsdax()
+{
+       local ops=$(_normalize_mount_options "$MOUNT_OPTIONS")
+
+       echo $ops | egrep -qw "dax(=always| |$)" && return 0
+       echo $ops | grep -qw "dax=never" && return 1
+
+       local sysfs="/sys/block/$(_short_dev $SCRATCH_DEV)"
+       test -e "${sysfs}/dax" && return 0
+       test "$(cat "${sysfs}/queue/dax" 2>/dev/null)" = "1" && return 0
+       return 1
+}
+
 # this test requires a specific device mapper target
 _require_dm_target()
 {
        local target=$1
+       local fsdax
+       local bdevdax
 
        # require SCRATCH_DEV to be a valid block device with sane BLKFLSBUF
        # behaviour
@@ -1867,8 +2051,7 @@ _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
+       if __scratch_uses_fsdax; then
                case $target in
                stripe|linear|log-writes)
                        ;;
@@ -1884,6 +2067,60 @@ _require_dm_target()
        if [ $? -ne 0 ]; then
                _notrun "This test requires dm $target support"
        fi
+
+       # dm-error cannot handle the zone information
+       #
+       # dm-snapshot and dm-thin-pool cannot ensure sequential writes on
+       # the backing device
+       case $target in
+       error|snapshot|thin-pool)
+               _require_non_zoned_device ${SCRATCH_DEV}
+               ;;
+       esac
+}
+
+_zone_type()
+{
+       local target=$1
+       if [ -z $target ]; then
+               echo "Usage: _zone_type <device>"
+               exit 1
+       fi
+       local sdev=`_short_dev $target`
+
+       if [ -e /sys/block/${sdev}/queue/zoned ]; then
+               cat /sys/block/${sdev}/queue/zoned
+       else
+               echo none
+       fi
+}
+
+_require_zoned_device()
+{
+       local target=$1
+       if [ -z $target ]; then
+               echo "Usage: _require_zoned_device <device>"
+               exit 1
+       fi
+
+       local type=`_zone_type ${target}`
+       if [ "${type}" = "none" ]; then
+               _notrun "this test require zoned block device"
+       fi
+}
+
+_require_non_zoned_device()
+{
+       local target=$1
+       if [ -z $target ]; then
+               echo "Usage: _require_non_zoned_device <device>"
+               exit 1
+       fi
+
+       local type=`_zone_type ${target}`
+       if [ "${type}" != "none" ]; then
+               _notrun "this test require non-zoned block device"
+       fi
 }
 
 # this test requires the ext4 kernel support crc feature on scratch device
@@ -1955,6 +2192,56 @@ _require_aiodio()
     _require_odirect
 }
 
+# this test requires that the kernel supports IO_URING
+_require_io_uring()
+{
+       $here/src/feature -R
+       case $? in
+       0)
+               ;;
+       1)
+               _notrun "kernel does not support IO_URING"
+               ;;
+       *)
+               _fail "unexpected error testing for IO_URING support"
+               ;;
+       esac
+}
+
+# test whether the mount_setattr syscall is available
+_require_mount_setattr()
+{
+       $here/src/feature -r
+       case $? in
+       0)
+               ;;
+       1)
+               _notrun "kernel does not support mount_setattr syscall"
+               ;;
+       *)
+               _fail "unexpected error testing for mount_setattr support"
+               ;;
+       esac
+}
+
+# test whether idmapped mounts are supported
+_require_idmapped_mounts()
+{
+        IDMAPPED_MOUNTS_TEST=$here/src/idmapped-mounts/idmapped-mounts
+        [ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "idmapped-mounts utilities required"
+
+       _require_mount_setattr
+
+       $here/src/idmapped-mounts/idmapped-mounts --supported \
+               --device "$TEST_DEV" \
+               --mount "$TEST_DIR" \
+               --fstype "$FSTYP"
+
+       if [ $? -ne 0 ]; then
+               _notrun "idmapped-mounts not support by $FSTYP"
+       fi
+}
+
 # this test requires that a test program exists under src/
 # $1 - command (require)
 #
@@ -2029,7 +2316,7 @@ _filesystem_timestamp_range()
                echo "0 $u32max"
                ;;
        xfs)
-               echo "$s32min $s32max"
+               _xfs_timestamp_range "$device"
                ;;
        btrfs)
                echo "$s64min $s64max"
@@ -2076,18 +2363,61 @@ _cat_group()
        cat /etc/group
 }
 
-# check for a user on the machine, fsgqa as default
+# check if a user exists in the system
+#
+_require_user_exists()
+{
+       local user=$1
+       _cat_passwd | grep -q "^$user:"
+       [ "$?" == "0" ] || _notrun "$user user not defined."
+}
+
+# check if a user exists and is able to execute commands.
+# Uses 'fsgqa' user as default.
 #
 _require_user()
 {
-    qa_user=fsgqa
-    if [ -n "$1" ];then
-        qa_user=$1
-    fi
-    _cat_passwd | grep -q $qa_user
-    [ "$?" == "0" ] || _notrun "$qa_user user not defined."
-    echo /bin/true | su $qa_user
-    [ "$?" == "0" ] || _notrun "$qa_user cannot execute commands."
+       qa_user=fsgqa
+       if [ -n "$1" ];then
+               qa_user=$1
+       fi
+       _require_user_exists $qa_user
+       echo /bin/true | su $qa_user
+       [ "$?" == "0" ] || _notrun "$qa_user cannot execute commands."
+}
+
+# check for a chown support
+#
+_require_chown()
+{
+       local rnd_uid=4242
+       local file="$TEST_DIR/chown_testfile"
+
+       rm -f $file
+       touch $file
+       chown ${rnd_uid}:${rnd_uid} $file >/dev/null 2>&1 \
+               || _notrun "chown is not supported ${FSTYP}"
+}
+
+
+# check for a chmod support
+# Since chmod sometimes fails silently actual functionality test is done
+#
+_require_chmod()
+{
+       local file="$TEST_DIR/chmod_testfile"
+
+       rm -f $file
+       touch $file
+
+       # get original file mode
+       local mode=`stat --format="0%a" $file`
+       # flip the user's read bit
+       let mode^=0400
+       chmod `printf '%o' "$mode"` $file
+       # check that the chmod actually flipped the bit
+       [ `stat --format="0%a" $file` == `printf '0%o' "$mode"` ] \
+               || _notrun "chmod is not supported ${FSTYP}"
 }
 
 # check for a group on the machine, fsgqa as default
@@ -2316,9 +2646,11 @@ _require_odirect()
        rm -f $testfile 2>&1 > /dev/null
 }
 
+# Format a swapfile and return its size in bytes
 _format_swapfile() {
        local fname="$1"
        local sz="$2"
+       local swap_log=""
 
        rm -f "$fname"
        touch "$fname"
@@ -2326,7 +2658,18 @@ _format_swapfile() {
        # Swap files must be nocow on Btrfs.
        $CHATTR_PROG +C "$fname" > /dev/null 2>&1
        _pwrite_byte 0x61 0 "$sz" "$fname" >> $seqres.full
-       $MKSWAP_PROG "$fname" >> $seqres.full
+       # Ignore permission complaints on filesystems that don't support perms
+       swap_log=$($MKSWAP_PROG "$fname" 2>&1 | grep -v "insecure permission")
+       echo $swap_log >> $seqres.full
+
+       echo $swap_log | grep -oP '\w+(?= bytes)'
+}
+
+_swapon_file() {
+       local fname="$1"
+
+       # Ignore permission complaints on filesystems that don't support perms
+       $(swapon "$fname" 2> >(grep -v "insecure permissions" >&2))
 }
 
 # Check that the filesystem supports swapfiles
@@ -2351,12 +2694,29 @@ _require_scratch_swapfile()
        _scratch_mount
 
        # Minimum size for mkswap is 10 pages
-       _format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10))
+       _format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
 
-       if ! swapon "$SCRATCH_MNT/swap" >/dev/null 2>&1; then
-               _scratch_unmount
-               _notrun "swapfiles are not supported"
-       fi
+       # ext* has supported all variants of swap files since their
+       # introduction, so swapon should not fail.
+       case "$FSTYP" in
+       ext2|ext3|ext4)
+               if ! swapon "$SCRATCH_MNT/swap" >/dev/null 2>&1; then
+                       if _check_s_dax "$SCRATCH_MNT/swap" 1 >/dev/null; then
+                               _scratch_unmount
+                               _notrun "swapfiles are not supported"
+                       else
+                               _scratch_unmount
+                               _fail "swapon failed for $FSTYP"
+                       fi
+               fi
+               ;;
+       *)
+               if ! swapon "$SCRATCH_MNT/swap" >/dev/null 2>&1; then
+                       _scratch_unmount
+                       _notrun "swapfiles are not supported"
+               fi
+               ;;
+       esac
 
        swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
        _scratch_unmount
@@ -2435,10 +2795,10 @@ _fstyp_has_non_default_seek_data_hole()
                return 0
                ;;
        nfs*)
-               # NFSv2 and NFSv3 only support default behavior of SEEK_HOLE,
-               # while NFSv4 supports non-default behavior
-               local nfsvers=`_df_device $TEST_DEV | $AWK_PROG '{ print $2 }'`
-               [ "$nfsvers" = "nfs4" ]
+               # NFSv2, NFSv3, and NFSv4.0/4.1 only support default behavior of SEEK_HOLE,
+               # while NFSv4.2 supports non-default behavior
+               local nfsvers=`_mount() | grep $TEST_DEV | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
+               [ "$nfsvers" = "4.2" ]
                return $?
                ;;
        overlay)
@@ -2691,6 +3051,15 @@ _check_generic_filesystem()
     fi
     rm -f $tmp.fsck
 
+    if [ $ok -eq 0 ] && [ -n "$DUMP_CORRUPT_FS" ]; then
+        case "$FSTYP" in
+        ext*)
+            local flatdev="$(basename "$device")"
+            _ext4_metadump "$seqres.$flatdev.check.qcow2" "$device" compress
+            ;;
+        esac
+    fi
+
     if [ $ok -eq 0 ]
     then
         echo "*** mount output ***"            >>$seqres.full
@@ -3202,13 +3571,38 @@ _check_s_dax()
 {
        local target=$1
        local exp_s_dax=$2
+       local ret=0
 
        local attributes=$($XFS_IO_PROG -c 'statx -r' $target | awk '/stat.attributes / { print $3 }')
+
+       # The original attribute bit value, STATX_ATTR_DAX (0x2000), conflicted
+       # with STATX_ATTR_MOUNT_ROOT.  Therefore, STATX_ATTR_DAX was changed to
+       # 0x00200000.
+       #
+       # Because DAX tests do not run on root mounts, STATX_ATTR_MOUNT_ROOT
+       # should always be 0.  Check for the old flag and fail the test if that
+       # occurs.
+
+       if [ $(( attributes & 0x2000 )) -ne 0 ]; then
+               echo "$target has an unexpected STATX_ATTR_MOUNT_ROOT flag set"
+               echo "which used to be STATX_ATTR_DAX"
+               echo "     This test should not be running on the root inode..."
+               echo "     Does the kernel have the following patch?"
+               echo "     72d1249e2ffd uapi: fix statx attribute value overlap for DAX & MOUNT_ROOT"
+       fi
+
        if [ $exp_s_dax -eq 0 ]; then
-               (( attributes & 0x2000 )) && echo "$target has unexpected S_DAX flag"
+               if (( attributes & 0x00200000 )); then
+                       echo "$target has unexpected S_DAX flag"
+                       ret=1
+               fi
        else
-               (( attributes & 0x2000 )) || echo "$target doesn't have expected S_DAX flag"
+               if ! (( attributes & 0x00200000 )); then
+                       echo "$target doesn't have expected S_DAX flag"
+                       ret=2
+               fi
        fi
+       return $ret
 }
 
 _check_xflag()
@@ -3300,7 +3694,7 @@ _has_metadata_journaling()
        fi
 
        case "$FSTYP" in
-       ext2|vfat|msdos|udf|exfat)
+       ext2|vfat|msdos|udf|exfat|tmpfs)
                echo "$FSTYP does not support metadata journaling"
                return 1
                ;;
@@ -3312,7 +3706,7 @@ _has_metadata_journaling()
                        return 1
                }
                # ext4 might not load a journal
-               if _normalize_mount_options | grep -qw "noload"; then
+               if _normalize_mount_options "$MOUNT_OPTIONS" | grep -qw "noload"; then
                        echo "mount option \"noload\" not allowed in this test"
                        return 1
                fi
@@ -3476,20 +3870,20 @@ _require_cloner()
                _notrun "cloner binary not present at $CLONER_PROG"
 }
 
-# Normalize mount options from global $MOUNT_OPTIONS
-# Convert options like "-o opt1,opt2 -oopt3" to
-# "opt1 opt2 opt3"
+# Normalize mount options from the option string in $1
+# Convert options like "-o opt1,opt2 -oopt3" to "opt1 opt2 opt3"
 _normalize_mount_options()
 {
-       echo $MOUNT_OPTIONS | sed -n 's/-o\s*\(\S*\)/\1/gp'| sed 's/,/ /g'
+       echo "$1" | sed -n 's/-o\s*\(\S*\)/\1/gp'| sed 's/,/ /g'
 }
 
-# skip test if MOUNT_OPTIONS contains the given strings
+# skip test if $1 contains the given strings in trailing arguments
 # Both dax and dax=always are excluded if dax or dax=always is passed
-_exclude_scratch_mount_option()
+_exclude_mount_option()
 {
-       local mnt_opts=$(_normalize_mount_options)
+       local mnt_opts=$(_normalize_mount_options "$1")
 
+       shift
        while [ $# -gt 0 ]; do
                local pattern=$1
                echo "$pattern" | egrep -q "dax(=always|$)" && \
@@ -3501,11 +3895,21 @@ _exclude_scratch_mount_option()
        done
 }
 
+_exclude_scratch_mount_option()
+{
+       _exclude_mount_option "$MOUNT_OPTIONS" $@
+}
+
+_exclude_test_mount_option()
+{
+       _exclude_mount_option "$TEST_FS_MOUNT_OPTS" $@
+}
+
 _require_atime()
 {
        _exclude_scratch_mount_option "noatime"
        case $FSTYP in
-       nfs|cifs)
+       nfs|cifs|virtiofs)
                _notrun "atime related mount options have no effect on $FSTYP"
                ;;
        esac
@@ -3530,6 +3934,14 @@ _create_loop_device()
 {
        local file=$1 dev
        dev=`losetup -f --show $file` || _fail "Cannot assign $file to a loop device"
+
+       # Try to enable asynchronous directio mode on the loopback device so
+       # that writeback started by a filesystem mounted on the loop device
+       # won't be throttled by buffered writes to the lower filesystem.  This
+       # is a performance optimization for tests that want to write a lot of
+       # data, so it isn't required to work.
+       test -b "$dev" && losetup --direct-io=on $dev 2> /dev/null
+
        echo $dev
 }
 
@@ -3611,7 +4023,10 @@ _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"
+       local ret=$?
+       [ $ret -eq 22 ] && _notrun "Require fcntl setlease support"
+       [ "$FSTYP" == "nfs" -a $ret -eq 11 ] && \
+               _notrun "NFS requires delegation before setlease"
 }
 
 _require_ofd_locks()
@@ -3714,7 +4129,7 @@ _get_available_space()
 # return device size in kb
 _get_device_size()
 {
-       grep -w `_short_dev $1` /proc/partitions | awk '{print $3}'
+       echo $(($(blockdev --getsz $1) >> 1))
 }
 
 # Make sure we actually have dmesg checking set up.
@@ -3974,11 +4389,18 @@ _get_file_block_size()
                echo "Missing mount point argument for _get_file_block_size"
                exit 1
        fi
-       if [ "$FSTYP" = "ocfs2" ]; then
+
+       case "$FSTYP" in
+       "ocfs2")
                stat -c '%o' $1
-       else
+               ;;
+       "xfs")
+               _xfs_get_file_block_size $1
+               ;;
+       *)
                _get_block_size $1
-       fi
+               ;;
+       esac
 }
 
 # Get the minimum block size of an fs.
@@ -3991,6 +4413,17 @@ _get_block_size()
        stat -f -c %S $1
 }
 
+# Require that the fundamental allocation unit of a file is the same as the
+# filesystem block size.  The sole parameter must be the root dir of a
+# filesystem.
+_require_file_block_size_equals_fs_block_size()
+{
+       local file_alloc_unit="$(_get_file_block_size $1)"
+       local fs_block_size="$(_get_block_size $1)"
+       test "$file_alloc_unit" != "$fs_block_size" && \
+               _notrun "File allocation unit is larger than a filesystem block"
+}
+
 get_page_size()
 {
        echo $(getconf PAGE_SIZE)
@@ -4023,7 +4456,14 @@ run_fsx()
 _require_fs_sysfs()
 {
        local attr=$1
-       local dname=$(_short_dev $TEST_DEV)
+       local dname
+
+       case "$FSTYP" in
+       btrfs)
+               dname=$(findmnt -n -o UUID $TEST_DEV) ;;
+       *)
+               dname=$(_short_dev $TEST_DEV) ;;
+       esac
 
        if [ -z "$attr" -o -z "$dname" ];then
                _fail "Usage: _require_fs_sysfs <sysfs_attr_path>"
@@ -4061,7 +4501,14 @@ _set_fs_sysfs_attr()
                _fail "Usage: _set_fs_sysfs_attr <mounted_device> <attr> <content>"
        fi
 
-       local dname=$(_short_dev $dev)
+       local dname
+       case "$FSTYP" in
+       btrfs)
+               dname=$(findmnt -n -o UUID ${dev}) ;;
+       *)
+               dname=$(_short_dev $dev) ;;
+       esac
+
        echo "$content" > /sys/fs/${FSTYP}/${dname}/${attr}
 }
 
@@ -4082,7 +4529,14 @@ _get_fs_sysfs_attr()
                _fail "Usage: _get_fs_sysfs_attr <mounted_device> <attr>"
        fi
 
-       local dname=$(_short_dev $dev)
+       local dname
+       case "$FSTYP" in
+       btrfs)
+               dname=$(findmnt -n -o UUID ${dev}) ;;
+       *)
+               dname=$(_short_dev $dev) ;;
+       esac
+
        cat /sys/fs/${FSTYP}/${dname}/${attr}
 }
 
@@ -4272,13 +4726,22 @@ _check_xfs_scrub_does_unicode() {
 
        _supports_xfs_scrub "${mount}" "${dev}" || return 1
 
-       # We only care if xfs_scrub has unicode string support...
-       if ! type ldd > /dev/null 2>&1 || \
-          ! ldd "${XFS_SCRUB_PROG}" | grep -q libicui18n; then
-               return 1
+       # If the xfs_scrub binary contains the string "Unicode name.*%s", then
+       # we know that it has the ability to complain about improper Unicode
+       # names.
+       if strings "${XFS_SCRUB_PROG}" | grep -q 'Unicode name.*%s'; then
+               return 0
        fi
 
-       return 0
+       # If the xfs_scrub binary is linked against the libicui18n Unicode
+       # library, then we surmise that it contains the Unicode name checker.
+       if type ldd > /dev/null 2>&1 && \
+          ldd "${XFS_SCRUB_PROG}" 2> /dev/null | grep -q libicui18n; then
+               return 0
+       fi
+
+       # We could not establish that xfs_scrub supports unicode names.
+       return 1
 }
 
 # exfat timestamps start at 1980 and cannot be prior to epoch
@@ -4321,6 +4784,54 @@ _getcap()
        return ${PIPESTATUS[0]}
 }
 
+_require_od_endian_flag()
+{
+       od --endian=little < /dev/null > /dev/null 2>&1 || \
+               _notrun "od does not support endian flag"
+}
+
+# Skip this test unless the filesystem treats names (directory entries,
+# fs labels, and extended attribute names) as raw byte sequences.
+_require_names_are_bytes() {
+        case "$FSTYP" in
+        ext2|ext3|ext4|f2fs|xfs|btrfs)
+               # do nothing
+               ;;
+       *)
+                _notrun "$FSTYP does not allow unrestricted byte streams for names"
+               ;;
+        esac
+}
+
+_has_kernel_config()
+{
+       local option=$1
+       local uname=$(uname -r)
+       local config_list="$KCONFIG_PATH
+                    /proc/config.gz
+                    /lib/modules/$uname/build/.config
+                    /boot/config-$uname
+                    /lib/kernel/config-$uname"
+
+       for config in $config_list; do
+               [ ! -f $config ] && continue
+               [ $config = "/proc/config.gz" ] && break
+               grep -qE "^${option}=[my]" $config
+               return
+       done
+
+       [ ! -f $config ] && _notrun "Could not locate kernel config file"
+
+       # We can only get here with /proc/config.gz
+       _require_command "$GZIP_PROG" gzip
+       $GZIP_PROG -cd $config | grep -qE "^${option}=[my]"
+}
+
+_require_kernel_config()
+{
+       _has_kernel_config $1 || _notrun "Installed kernel not built with $1"
+}
+
 init_rc
 
 ################################################################################