| #! /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") |
| # |
| . ./common/preamble |
| _begin_fstest auto quick copy_range |
| |
| _register_cleanup "_cleanup" BUS |
| |
| # Override the default cleanup function. |
| _cleanup() |
| { |
| cd / |
| rm -rf $tmp.* |
| [ -n "$loop_dev" ] && _destroy_loop_device $loop_dev |
| } |
| |
| # Import common functions. |
| . ./common/filter |
| |
| |
| _require_test |
| _require_loop |
| # for mkfifo |
| _require_mknod |
| |
| # |
| # 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. |
| # |
| # Test both basic copy_range and copy_range -f availability |
| _require_xfs_io_command "copy_range" |
| _require_xfs_io_command "copy_range" "-f" |
| |
| testdir="$TEST_DIR/test-$seq" |
| rm -rf $testdir |
| mkdir $testdir |
| |
| $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 |
| loop_dev=`_create_loop_device $testdir/img` |
| $XFS_IO_PROG -c "copy_range -l 32k $testdir/file" $loop_dev |
| $XFS_IO_PROG -f -c "copy_range -l 32k $loop_dev" $testdir/copy |
| _destroy_loop_device $loop_dev |
| unset loop_dev |
| |
| 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 |