diff --git a/tests/generic/564 b/tests/generic/564 new file mode 100755 index 00000000..ee1786cc --- /dev/null +++ b/tests/generic/564 @@ -0,0 +1,134 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2018 Red Hat, Inc. All Rights Reserved. +# +# FS QA Test No. 564 +# +# Exercise copy_file_range() syscall error conditions. +# +# This is a regression test for kernel commit: +# 96e6e8f4a68d ("vfs: add missing checks to copy_file_range") +# +seq=`basename $0` +seqres=$RESULT_DIR/$seq +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 7 15 + +_cleanup() +{ + cd / + rm -rf $tmp.* + [ -z "$loopdev" ] || _destroy_loop_device $loopdev +} + +# get standard environment, filters and checks +. ./common/rc +. ./common/filter + +# real QA test starts here +_supported_os Linux +_supported_fs generic + +rm -f $seqres.full + +_require_test +_require_loop +# +# This test effectively requires xfs_io with these commits +# 2a42470b xfs_io: copy_file_range length is a size_t +# 1a05efba io: open pipes in non-blocking mode +# +# Without those commits test will hang on old kernel when copying +# very large size and when copying from a pipe. +# +# We require a new xfs_io feature of passing an open file as the +# copy source, as an indication that the test can run without hanging +# with large size argument and to avoid opening pipe in blocking mode. +# +_require_xfs_io_command "copy_range" "-f" + +testdir="$TEST_DIR/test-$seq" +rm -rf $testdir +mkdir $testdir + +rm -f $seqres.full + +$XFS_IO_PROG -f -c "pwrite -S 0x61 0 128k" $testdir/file >> $seqres.full 2>&1 + +echo source range overlaps destination range in same file returns EINVAL +$XFS_IO_PROG -f -c "copy_range -s 32k -d 48k -l 32k $testdir/file" $testdir/file + +echo +echo destination file O_RDONLY returns EBADF +$XFS_IO_PROG -f -r -c "copy_range -l 32k $testdir/file" $testdir/copy + +echo +echo destination file O_APPEND returns EBADF +$XFS_IO_PROG -f -a -c "copy_range -l 32k $testdir/file" $testdir/copy + +echo +echo source/destination as directory returns EISDIR +$XFS_IO_PROG -c "copy_range -l 32k $testdir/file" $testdir +$XFS_IO_PROG -f -c "copy_range -l 32k $testdir" $testdir/copy + +echo +echo source/destination as blkdev returns EINVAL +$XFS_IO_PROG -f -c "truncate 128k" $testdir/img >> $seqres.full 2>&1 +loopdev=`_create_loop_device $testdir/img` +$XFS_IO_PROG -c "copy_range -l 32k $testdir/file" $loopdev +$XFS_IO_PROG -f -c "copy_range -l 32k $loopdev" $testdir/copy +_destroy_loop_device $loopdev +loopdev= + +echo +echo source/destination as chardev returns EINVAL +$XFS_IO_PROG -c "copy_range -l 32k $testdir/file" /dev/null +$XFS_IO_PROG -f -c "copy_range -l 32k /dev/zero" $testdir/copy + +echo +echo source/destination as FIFO returns EINVAL +mkfifo $testdir/fifo +$XFS_IO_PROG -c "copy_range -l 32k $testdir/file" $testdir/fifo +# Pass input pipe as non-blocking open file to avoid old xfs_io (<4.20) +# opening the pipe in blocking mode and causing the test to hang +$XFS_IO_PROG -r -n -f -c "open $testdir/copy" -C "copy_range -l 32k -f 0" $testdir/fifo + +max_off=$((8 * 2**60 - 65536 - 1)) +min_off=65537 + +echo +echo length beyond 8EiB wraps around 0 returns EOVERFLOW +$XFS_IO_PROG -f -c "copy_range -l 10e -s $max_off $testdir/file" $testdir/copy +$XFS_IO_PROG -f -c "copy_range -l 10e -d $max_off $testdir/file" $testdir/copy + +echo +echo source range beyond 8TiB returns 0 +$XFS_IO_PROG -c "copy_range -s $max_off -l $min_off -d 0 $testdir/file" $testdir/copy + +echo +echo destination range beyond 8TiB returns EFBIG +$XFS_IO_PROG -c "copy_range -l $min_off -s 0 -d $max_off $testdir/file" $testdir/copy + +echo +echo destination larger than rlimit returns EFBIG +rm -f $testdir/copy +$XFS_IO_PROG -c "truncate 128k" $testdir/file + +# need a wrapper so the "File size limit exceeded" error can be filtered +do_rlimit_copy() +{ + $XFS_IO_PROG -f -c "copy_range -l 32k -s 0 -d 16m $testdir/file" $testdir/copy +} + +ulimit -f $((8 * 1024)) +ulimit -c 0 +do_rlimit_copy 2>&1 | grep -o "File size limit exceeded" +ulimit -f unlimited + +# success, all done +status=0 +exit diff --git a/tests/generic/564.out b/tests/generic/564.out new file mode 100644 index 00000000..848d3c70 --- /dev/null +++ b/tests/generic/564.out @@ -0,0 +1,37 @@ +QA output created by 564 +source range overlaps destination range in same file returns EINVAL +copy_range: Invalid argument + +destination file O_RDONLY returns EBADF +copy_range: Bad file descriptor + +destination file O_APPEND returns EBADF +copy_range: Bad file descriptor + +source/destination as directory returns EISDIR +copy_range: Is a directory +copy_range: Is a directory + +source/destination as blkdev returns EINVAL +copy_range: Invalid argument +copy_range: Invalid argument + +source/destination as chardev returns EINVAL +copy_range: Invalid argument +copy_range: Invalid argument + +source/destination as FIFO returns EINVAL +copy_range: Invalid argument +copy_range: Invalid argument + +length beyond 8EiB wraps around 0 returns EOVERFLOW +copy_range: Value too large for defined data type +copy_range: Value too large for defined data type + +source range beyond 8TiB returns 0 + +destination range beyond 8TiB returns EFBIG +copy_range: File too large + +destination larger than rlimit returns EFBIG +File size limit exceeded diff --git a/tests/generic/group b/tests/generic/group index 284a1d93..1a3284ab 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -566,3 +566,4 @@ 561 auto stress dedupe 562 auto clone 563 auto quick +564 auto quick copy_range