summaryrefslogtreecommitdiffstats
path: root/tests/cp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
commite1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch)
treef5cc731bedcac0fb7fe14d952e4581e749f8bb87 /tests/cp
parentInitial commit. (diff)
downloadcoreutils-upstream.tar.xz
coreutils-upstream.zip
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/cp')
-rwxr-xr-xtests/cp/abuse.sh50
-rwxr-xr-xtests/cp/acl.sh60
-rwxr-xr-xtests/cp/attr-existing.sh44
-rwxr-xr-xtests/cp/backup-1.sh37
-rwxr-xr-xtests/cp/backup-dir.sh38
-rwxr-xr-xtests/cp/backup-is-src.sh36
-rwxr-xr-xtests/cp/capability.sh52
-rwxr-xr-xtests/cp/copy-FMR.sh31
-rwxr-xr-xtests/cp/cp-HL.sh40
-rwxr-xr-xtests/cp/cp-a-selinux.sh228
-rwxr-xr-xtests/cp/cp-deref.sh34
-rwxr-xr-xtests/cp/cp-i.sh71
-rwxr-xr-xtests/cp/cp-mv-backup.sh92
-rwxr-xr-xtests/cp/cp-mv-enotsup-xattr.sh132
-rwxr-xr-xtests/cp/cp-parents.sh75
-rwxr-xr-xtests/cp/cross-dev-symlink.sh38
-rwxr-xr-xtests/cp/debug.sh32
-rwxr-xr-xtests/cp/deref-slink.sh28
-rwxr-xr-xtests/cp/dir-rm-dest.sh35
-rwxr-xr-xtests/cp/dir-slash.sh35
-rwxr-xr-xtests/cp/dir-vs-file.sh33
-rwxr-xr-xtests/cp/existing-perm-dir.sh31
-rwxr-xr-xtests/cp/existing-perm-race.sh94
-rwxr-xr-xtests/cp/fail-perm.sh66
-rwxr-xr-xtests/cp/file-perm-race.sh58
-rwxr-xr-xtests/cp/into-self.sh57
-rwxr-xr-xtests/cp/link-deref.sh126
-rwxr-xr-xtests/cp/link-heap.sh42
-rwxr-xr-xtests/cp/link-no-deref.sh29
-rwxr-xr-xtests/cp/link-preserve.sh91
-rwxr-xr-xtests/cp/link-symlink.sh41
-rwxr-xr-xtests/cp/link.sh31
-rwxr-xr-xtests/cp/nfs-removal-race.sh75
-rwxr-xr-xtests/cp/no-ctx.sh65
-rwxr-xr-xtests/cp/no-deref-link1.sh37
-rwxr-xr-xtests/cp/no-deref-link2.sh37
-rwxr-xr-xtests/cp/no-deref-link3.sh34
-rwxr-xr-xtests/cp/parent-perm-race.sh59
-rwxr-xr-xtests/cp/parent-perm.sh53
-rwxr-xr-xtests/cp/perm.sh77
-rwxr-xr-xtests/cp/preserve-2.sh29
-rwxr-xr-xtests/cp/preserve-gid.sh146
-rwxr-xr-xtests/cp/preserve-link.sh92
-rwxr-xr-xtests/cp/preserve-mode.sh66
-rwxr-xr-xtests/cp/preserve-slink-time.sh51
-rwxr-xr-xtests/cp/proc-short-read.sh43
-rwxr-xr-xtests/cp/proc-zero-len.sh47
-rwxr-xr-xtests/cp/r-vs-symlink.sh41
-rwxr-xr-xtests/cp/reflink-auto.sh45
-rwxr-xr-xtests/cp/reflink-perm.sh45
-rwxr-xr-xtests/cp/same-file.sh255
-rwxr-xr-xtests/cp/slink-2-slink.sh32
-rwxr-xr-xtests/cp/sparse-2.sh56
-rwxr-xr-xtests/cp/sparse-extents-2.sh116
-rwxr-xr-xtests/cp/sparse-extents.sh83
-rwxr-xr-xtests/cp/sparse-perf.sh40
-rwxr-xr-xtests/cp/sparse-to-pipe.sh38
-rwxr-xr-xtests/cp/sparse.sh77
-rwxr-xr-xtests/cp/special-bits.sh51
-rwxr-xr-xtests/cp/special-f.sh33
-rwxr-xr-xtests/cp/src-base-dot.sh28
-rwxr-xr-xtests/cp/symlink-slash.sh36
-rwxr-xr-xtests/cp/thru-dangling.sh51
63 files changed, 3825 insertions, 0 deletions
diff --git a/tests/cp/abuse.sh b/tests/cp/abuse.sh
new file mode 100755
index 0000000..8c3eee1
--- /dev/null
+++ b/tests/cp/abuse.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+# ensure that cp does not write through a just-copied symlink
+
+# Copyright (C) 2007-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir a b c || framework_failure_
+ln -s ../t a/1 || framework_failure_
+echo payload > b/1 || framework_failure_
+
+echo "cp: will not copy 'b/1' through just-created symlink 'c/1'" \
+ > exp || framework_failure_
+
+# Check both cases: a dangling symlink, and one pointing to a writable file.
+
+for i in dangling-dest existing-dest; do
+ test $i = existing-dest && echo i > t
+ test $i = dangling-dest && rm -f t
+
+ cp -dR a/1 b/1 c 2> out && fail=1
+
+ compare exp out || fail=1
+
+ # When the destination is a dangling symlink,
+ # ensure that cp does not create it.
+ test $i = dangling-dest \
+ && test -f t && fail=1
+
+ # When the destination symlink points to a writable file,
+ # ensure that cp does not change it.
+ test $i = existing-dest \
+ && case $(cat t) in i);; *) fail=1;; esac
+done
+
+Exit $fail
diff --git a/tests/cp/acl.sh b/tests/cp/acl.sh
new file mode 100755
index 0000000..6d0170d
--- /dev/null
+++ b/tests/cp/acl.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# copy files/directories across file system boundaries
+# and make sure acls are preserved appropriately
+
+# Copyright (C) 2005-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_acl_
+
+# Skip this test if cp was built without ACL support:
+grep '^#define USE_ACL 1' $CONFIG_HEADER > /dev/null ||
+ skip_ "insufficient ACL support"
+
+mkdir -p a b || framework_failure_
+touch a/file || framework_failure_
+
+# Ensure that setfacl and getfacl work on this file system.
+skip=no
+acl1=$(cd a && getfacl file) || skip=yes
+setfacl -m user:bin:rw- a/file 2> /dev/null || skip=yes
+test $skip = yes &&
+ skip_ "'.' is not on a suitable file system for this test"
+
+# copy a file without preserving permissions
+cp a/file b/ || fail=1
+acl2=$(cd b && getfacl file) || framework_failure_
+test "$acl1" = "$acl2" || fail=1
+
+# Update with acl set above
+acl1=$(cd a && getfacl file) || framework_failure_
+
+# copy a file, preserving permissions
+cp -p a/file b/ || fail=1
+acl2=$(cd b && getfacl file) || framework_failure_
+test "$acl1" = "$acl2" || fail=1
+
+# copy a file, preserving permissions, with --attributes-only
+echo > a/file || framework_failure_ # add some data
+test -s a/file || framework_failure_
+cp -p --attributes-only a/file b/ || fail=1
+compare /dev/null b/file || fail=1
+acl2=$(cd b && getfacl file) || framework_failure_
+test "$acl1" = "$acl2" || fail=1
+
+Exit $fail
diff --git a/tests/cp/attr-existing.sh b/tests/cp/attr-existing.sh
new file mode 100755
index 0000000..47772ab
--- /dev/null
+++ b/tests/cp/attr-existing.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Make sure cp --attributes-only doesn't truncate existing data
+
+# Copyright 2012-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+printf '1' > file1 || framework_failure_
+printf '2' > file2 || framework_failure_
+printf '2' > file2.exp || framework_failure_
+
+cp --attributes-only file1 file2 || fail=1
+cmp file2 file2.exp || fail=1
+
+# coreutils v8.32 and before would remove destination files
+# if hardlinked or the source was not a regular file.
+ln file2 link2 || framework_failure_
+cp -a --attributes-only file1 file2 || fail=1
+cmp file2 file2.exp || fail=1
+
+ln -s file1 sym1 || framework_failure_
+returns_ 1 cp -a --attributes-only sym1 file2 || fail=1
+cmp file2 file2.exp || fail=1
+
+# One can still force removal though
+cp -a --remove-destination --attributes-only sym1 file2 || fail=1
+test -L file2 || fail=1
+cmp file1 file2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/backup-1.sh b/tests/cp/backup-1.sh
new file mode 100755
index 0000000..613d3a4
--- /dev/null
+++ b/tests/cp/backup-1.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Test cp backup.
+
+# Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+suffix=.b
+file=F
+file_backup="$file$suffix"
+
+echo test > $file || framework_failure_
+
+# Specify both version control and suffix so the environment variables
+# (possibly set by the user running these tests) aren't used.
+cp --force --backup=simple --suffix=$suffix $file $file || fail=1
+cp -T --force --backup=simple --suffix=$suffix $file $file || fail=1
+
+test -f $file || fail=1
+test -f $file_backup || fail=1
+compare $file $file_backup > /dev/null || fail=1
+
+Exit $fail
diff --git a/tests/cp/backup-dir.sh b/tests/cp/backup-dir.sh
new file mode 100755
index 0000000..c05d956
--- /dev/null
+++ b/tests/cp/backup-dir.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Ensure that cp -b handles directories appropriately
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir x y || framework_failure_
+
+
+cp -a x y || fail=1
+
+# This would mistakenly create a backup of y/x (y/x~) in coreutils-6.3.
+cp -ab x y || fail=1
+test -d y/x || fail=1
+test -d y/x~ && fail=1
+
+# Bug 62607.
+# This would fail to backup using rename, and thus fail to replace the file
+mkdir -p src/foo dst/foo || framework_failure_
+touch src/foo/bar dst/foo/bar || framework_failure_
+cp --recursive --backup src/* dst || fail=1
+
+Exit $fail
diff --git a/tests/cp/backup-is-src.sh b/tests/cp/backup-is-src.sh
new file mode 100755
index 0000000..6534f5e
--- /dev/null
+++ b/tests/cp/backup-is-src.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Test cp backup to source file.
+
+# Copyright (C) 1998-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+echo a > a || framework_failure_
+echo a-tilde > a~ || framework_failure_
+
+# This cp command should exit nonzero.
+cp --b=simple a~ a > out 2>&1 && fail=1
+
+sed "s,cp:,XXX:," out > out2
+
+cat > exp <<\EOF
+XXX: backing up 'a' might destroy source; 'a~' not copied
+EOF
+
+compare exp out2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/capability.sh b/tests/cp/capability.sh
new file mode 100755
index 0000000..be3a7f8
--- /dev/null
+++ b/tests/cp/capability.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# Ensure cp --preserves copies capabilities
+
+# Copyright (C) 2010-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_root_
+working_umask_or_skip_
+
+
+grep '^#define HAVE_CAP 1' $CONFIG_HEADER > /dev/null \
+ || skip_ "configured without libcap support"
+
+(setcap --help) 2>&1 |grep 'usage: setcap' > /dev/null \
+ || skip_ "setcap utility not found"
+(getcap --help) 2>&1 |grep 'usage: getcap' > /dev/null \
+ || skip_ "getcap utility not found"
+
+
+touch file || framework_failure_
+chown $NON_ROOT_USERNAME file || framework_failure_
+
+setcap 'cap_net_bind_service=ep' file ||
+ skip_ "setcap doesn't work"
+getcap file | grep cap_net_bind_service >/dev/null ||
+ skip_ "getcap doesn't work"
+
+cp --preserve=xattr file copy1 || fail=1
+
+# Before coreutils 8.5 the capabilities would not be preserved,
+# as the owner was set _after_ copying xattrs, thus clearing any capabilities.
+cp --preserve=all file copy2 || fail=1
+
+for file in copy1 copy2; do
+ getcap $file | grep cap_net_bind_service >/dev/null || fail=1
+done
+
+Exit $fail
diff --git a/tests/cp/copy-FMR.sh b/tests/cp/copy-FMR.sh
new file mode 100755
index 0000000..56d2d64
--- /dev/null
+++ b/tests/cp/copy-FMR.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Trigger a free-memory read bug in cp from coreutils-[8.11..8.19]
+
+# Copyright (C) 2012-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_valgrind_
+require_perl_
+
+# Trigger FMR in fiemap logic from v8.11..v8.19
+$PERL -e 'for (1..600) { sysseek (*STDOUT, 4096, 1)' \
+ -e '&& syswrite (*STDOUT, "a" x 1024) or die "$!"}' > j || fail=1
+valgrind --quiet --error-exitcode=3 cp --reflink=never j j2 || fail=1
+cmp j j2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/cp-HL.sh b/tests/cp/cp-HL.sh
new file mode 100755
index 0000000..d5bbedd
--- /dev/null
+++ b/tests/cp/cp-HL.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# test cp's -H and -L options
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir src-dir dest-dir || framework_failure_
+echo f > f || framework_failure_
+ln -s f slink || framework_failure_
+ln -s no-such-file src-dir/slink || framework_failure_
+
+
+cp -H -R slink src-dir dest-dir || fail=1
+test -d src-dir || fail=1
+test -d dest-dir/src-dir || fail=1
+
+# Expect this to succeed since this slink is not a symlink
+cat dest-dir/slink > /dev/null 2>&1 || fail=1
+
+# Expect this to fail since *this* slink is a dangling symlink.
+returns_ 1 cat dest-dir/src-dir/slink >/dev/null 2>&1 || fail=1
+
+# FIXME: test -L, too.
+
+Exit $fail
diff --git a/tests/cp/cp-a-selinux.sh b/tests/cp/cp-a-selinux.sh
new file mode 100755
index 0000000..8679a01
--- /dev/null
+++ b/tests/cp/cp-a-selinux.sh
@@ -0,0 +1,228 @@
+#!/bin/sh
+# Ensure that cp -Z, -a and cp --preserve=context work properly.
+# In particular, test on a writable NFS partition.
+# Check also locally if --preserve=context, -a and --preserve=all
+# does work
+
+# Copyright (C) 2007-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_root_
+require_selinux_
+
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/mnt"; }
+
+# This context is special: it works even when mcstransd isn't running.
+ctx='root:object_r:tmp_t'
+mls_enabled_ && ctx="$ctx:s0"
+
+# Check basic functionality - before check on fixed context mount
+touch c || framework_failure_
+chcon $ctx c || skip_ "Failed to set context: $ctx"
+cp -a c d 2>err || framework_failure_
+cp --preserve=context c e || framework_failure_
+cp --preserve=all c f || framework_failure_
+ls -Z d | grep $ctx || fail=1
+# there must be no stderr output for -a
+compare /dev/null err || fail=1
+ls -Z e | grep $ctx || fail=1
+ls -Z f | grep $ctx || fail=1
+rm -f f
+
+# Check handling of existing dirs which requires specific handling
+# due to recursion, and was handled incorrectly in coreutils-8.22
+# Note standard permissions are updated for existing directories
+# in the destination, so SELinux contexts should be updated too.
+mkdir -p backup/existing_dir/ || framework_failure_
+ls -Zd backup/existing_dir > ed_ctx || fail=1
+grep $ctx ed_ctx && framework_failure_
+touch backup/existing_dir/file || framework_failure_
+chcon $ctx backup/existing_dir/file || framework_failure_
+# Set the dir context to ensure it is reset
+mkdir -p --context="$ctx" restore/existing_dir || framework_failure_
+# Copy and ensure existing directories updated
+cp -a backup/. restore/ || fail=1
+ls -Zd restore/existing_dir > ed_ctx || fail=1
+grep $ctx ed_ctx &&
+ { ls -lZd restore/existing_dir; fail=1; }
+
+# Check context preserved with directories created with --parents,
+# which was not handled before coreutils-8.27
+mkdir -p parents/a/b || framework_failure_
+ls -Zd parents/a/b > ed_ctx || fail=1
+grep $ctx ed_ctx && framework_failure_
+touch parents/a/b/file || framework_failure_
+chcon $ctx parents/a/b || framework_failure_
+# Set the dir context to ensure it is reset
+mkdir -p --context="$ctx" parents_dest/parents/a || framework_failure_
+# Copy and ensure existing directories updated
+cp -r --parents --preserve=context parents/a/b/file parents_dest || fail=1
+# Check new context
+ls -Zd parents_dest/parents/a/b > ed_ctx || fail=1
+grep $ctx ed_ctx ||
+ { ls -lZd parents_dest/parents/a/b; fail=1; }
+# Check updated context
+ls -Zd parents_dest/parents/a > ed_ctx || fail=1
+grep $ctx ed_ctx &&
+ { ls -lZd parents_dest/parents/a; fail=1; }
+
+# Check restorecon (-Z) functionality for file and directory
+# Also make a dir with our known context
+mkdir c_d || framework_failure_
+chcon $ctx c_d || framework_failure_
+# Get the type of this known context for file and dir for tracing
+old_type_f=$(get_selinux_type c)
+old_type_d=$(get_selinux_type c_d)
+# Setup copies for manipulation with restorecon
+# and get the adjusted type for comparison
+cp -a c Z1 || fail=1
+cp -a c_d Z1_d || fail=1
+if restorecon Z1 Z1_d 2>restorecon.err \
+ && compare /dev/null restorecon.err; then
+ new_type_f=$(get_selinux_type Z1)
+ new_type_d=$(get_selinux_type Z1_d)
+
+ # Ensure -Z sets the type like restorecon does
+ cp -Z c Z2 || fail=1
+ cpZ_type_f=$(get_selinux_type Z2)
+ test "$cpZ_type_f" = "$new_type_f" || fail=1
+
+ # Ensure -Z overrides -a and that dirs are handled too
+ cp -aZ c Z3 || fail=1
+ cp -aZ c_d Z3_d || fail=1
+ cpaZ_type_f=$(get_selinux_type Z3)
+ cpaZ_type_d=$(get_selinux_type Z3_d)
+ test "$cpaZ_type_f" = "$new_type_f" || fail=1
+ test "$cpaZ_type_d" = "$new_type_d" || fail=1
+
+ # Ensure -Z sets the type for existing files
+ mkdir -p existing/c_d || framework_failure_
+ touch existing/c || framework_failure_
+ cp -aZ c c_d existing || fail=1
+ cpaZ_type_f=$(get_selinux_type existing/c)
+ cpaZ_type_d=$(get_selinux_type existing/c_d)
+ test "$cpaZ_type_f" = "$new_type_f" || fail=1
+ test "$cpaZ_type_d" = "$new_type_d" || fail=1
+fi
+
+skip=0
+# Create a file system, then mount it with the context=... option.
+dd if=/dev/zero of=blob bs=8192 count=200 || skip=1
+mkdir mnt || skip=1
+mkfs -t ext2 -F blob ||
+ skip_ "failed to create an ext2 file system"
+
+mount -oloop,context=$ctx blob mnt || skip=1
+test $skip = 1 \
+ && skip_ "insufficient mount/ext2 support"
+
+cd mnt || framework_failure_
+
+# Create files with hopefully different contexts
+echo > ../f || framework_failure_
+echo > g || framework_failure_
+test "$(stat -c%C ../f)" = "$(stat -c%C g)" &&
+ skip_ "files on separate file systems have the same security context"
+
+# /bin/cp from coreutils-6.7-3.fc7 would fail this test by letting cp
+# succeed (giving no diagnostics), yet leaving the destination file empty.
+cp -a ../f g 2>err || fail=1
+test -s g || fail=1 # The destination file must not be empty.
+compare /dev/null err || fail=1
+
+# =====================================================
+# Here, we expect cp to succeed and not warn with "Operation not supported"
+rm -f g
+echo > g
+cp --preserve=all ../f g 2>err || fail=1
+test -s g || fail=1
+grep "Operation not supported" err && fail=1
+
+# =====================================================
+# The same as above except destination does not exist
+rm -f g
+cp --preserve=all ../f g 2>err || fail=1
+test -s g || fail=1
+grep "Operation not supported" err && fail=1
+
+# An alternative to the following approach would be to run in a confined
+# domain (maybe creating/loading it) that lacks the required permissions
+# to the file type.
+# Note: this test could also be run by a regular (non-root) user in an
+# NFS mounted directory. When doing that, I get this diagnostic:
+# cp: failed to set the security context of 'g' to 'system_u:object_r:nfs_t': \
+# Operation not supported
+cat <<\EOF > exp || framework_failure_
+cp: failed to set the security context of
+EOF
+
+rm -f g
+echo > g
+# =====================================================
+# Here, we expect cp to fail, because it cannot set the SELinux
+# security context through NFS or a mount with fixed context.
+cp --preserve=context ../f g 2> out && fail=1
+# Here, we *do* expect the destination to be empty.
+compare /dev/null g || fail=1
+sed "s/ .g'.*//" out > k
+mv k out
+compare exp out || fail=1
+
+rm -f g
+echo > g
+# Check if -a option doesn't silence --preserve=context option diagnostics
+cp -a --preserve=context ../f g 2> out2 && fail=1
+# Here, we *do* expect the destination to be empty.
+compare /dev/null g || fail=1
+sed "s/ .g'.*//" out2 > k
+mv k out2
+compare exp out2 || fail=1
+
+for no_g_cmd in '' 'rm -f g'; do
+ # restorecon equivalent. Note even though the context
+ # returned from matchpathcon() will not match $ctx
+ # the resulting ENOTSUP warning will be suppressed.
+
+ # With absolute path
+ $no_g_cmd
+ cp -Z ../f $(realpath g) || fail=1
+ # With relative path
+ $no_g_cmd
+ cp -Z ../f g || fail=1
+ # -Z overrides -a
+ $no_g_cmd
+ cp -Z -a ../f g || fail=1
+ # -Z doesn't take an arg
+ $no_g_cmd
+ returns_ 1 cp -Z "$ctx" ../f g || fail=1
+
+ # Explicit context
+ $no_g_cmd
+ # Explicitly defaulting to the global $ctx should work
+ cp --context="$ctx" ../f g || fail=1
+ # --context overrides -a
+ $no_g_cmd
+ cp -a --context="$ctx" ../f g || fail=1
+done
+
+# Mutually exclusive options
+returns_ 1 cp -Z --preserve=context ../f g || fail=1
+returns_ 1 cp --preserve=context -Z ../f g || fail=1
+returns_ 1 cp --preserve=context --context="$ctx" ../f g || fail=1
+
+Exit $fail
diff --git a/tests/cp/cp-deref.sh b/tests/cp/cp-deref.sh
new file mode 100755
index 0000000..783ec07
--- /dev/null
+++ b/tests/cp/cp-deref.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# cp -RL dir1 dir2' must handle the case in which each of dir1 and dir2
+# contain a symlink pointing to some third directory.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir a b c d || framework_failure_
+ln -s ../c a || framework_failure_
+ln -s ../c b || framework_failure_
+
+
+# Before coreutils-5.94, the following would fail with this message:
+# cp: will not create hard link 'd/b/c' to directory 'd/a/c'
+cp -RL a b d || fail=1
+test -d a/c || fail=1
+test -d b/c || fail=1
+
+Exit $fail
diff --git a/tests/cp/cp-i.sh b/tests/cp/cp-i.sh
new file mode 100755
index 0000000..a9178a9
--- /dev/null
+++ b/tests/cp/cp-i.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# Test whether cp -i prompts in the right place.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir -p a b/a/c || framework_failure_
+touch a/c || framework_failure_
+
+
+# coreutils 6.2 cp would neglect to prompt in this case.
+echo n | returns_ 1 cp -iR a b 2>/dev/null || fail=1
+
+# test miscellaneous combinations of -f -i -n parameters
+touch c d || framework_failure_
+echo "'c' -> 'd'" > out_copy || framework_failure_
+echo "cp: not replacing 'd'" > err_skip || framework_failure_
+touch out_empty || framework_failure_
+
+# ask for overwrite, answer no
+echo n | returns_ 1 cp -vi c d 2>/dev/null > out1 || fail=1
+compare out1 out_empty || fail=1
+
+# ask for overwrite, answer yes
+echo y | cp -vi c d 2>/dev/null > out2 || fail=1
+compare out2 out_copy || fail=1
+
+# -i wins over -n
+echo y | cp -vni c d 2>/dev/null > out3 || fail=1
+compare out3 out_copy || fail=1
+
+# -n wins over -i
+echo y | returns_ 1 cp -vin c d 2>/dev/null > out4 || fail=1
+compare out4 out_empty || fail=1
+
+# -n wins over -i non verbose
+echo y | returns_ 1 cp -in c d 2>err4 > out4 || fail=1
+compare err4 err_skip || fail=1
+compare out4 out_empty || fail=1
+
+# ask for overwrite, answer yes
+echo y | cp -vfi c d 2>/dev/null > out5 || fail=1
+compare out5 out_copy || fail=1
+
+# do not ask, prevent from overwrite
+echo n | returns_ 1 cp -vfn c d 2>/dev/null > out6 || fail=1
+compare out6 out_empty || fail=1
+
+# do not ask, prevent from overwrite
+echo n | returns_ 1 cp -vnf c d 2>/dev/null > out7 || fail=1
+compare out7 out_empty || fail=1
+
+# options --backup and --no-clobber are mutually exclusive
+returns_ 1 cp -bn c d 2>/dev/null || fail=1
+
+Exit $fail
diff --git a/tests/cp/cp-mv-backup.sh b/tests/cp/cp-mv-backup.sh
new file mode 100755
index 0000000..5c3e4fc
--- /dev/null
+++ b/tests/cp/cp-mv-backup.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+# Test basic --backup functionality for both cp and mv.
+
+# Copyright (C) 1999-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+umask 022
+
+# Be careful to close $actual before removing the containing directory.
+# Use '1>&2' rather than '1<&-' since the latter appears not to work
+# with /bin/sh from powerpc-ibm-aix4.2.0.0.
+
+actual=actual
+expected=expected
+
+exec 3>&1 1> $actual
+
+for prog in cp mv; do
+ for initial_files in 'x' 'x y' 'x y y~' 'x y y.~1~' 'x y y~ y.~1~'; do
+ for opt in none off numbered t existing nil simple never; do
+ touch $initial_files
+ $prog --backup=$opt x y || fail=1
+ echo $initial_files $opt: $(ls [xy]*); rm -f x y y~ y.~?~
+ done
+ done
+done
+
+cat <<\EOF > $expected-tmp
+x none: x y
+x off: x y
+x numbered: x y
+x t: x y
+x existing: x y
+x nil: x y
+x simple: x y
+x never: x y
+x y none: x y
+x y off: x y
+x y numbered: x y y.~1~
+x y t: x y y.~1~
+x y existing: x y y~
+x y nil: x y y~
+x y simple: x y y~
+x y never: x y y~
+x y y~ none: x y y~
+x y y~ off: x y y~
+x y y~ numbered: x y y.~1~ y~
+x y y~ t: x y y.~1~ y~
+x y y~ existing: x y y~
+x y y~ nil: x y y~
+x y y~ simple: x y y~
+x y y~ never: x y y~
+x y y.~1~ none: x y y.~1~
+x y y.~1~ off: x y y.~1~
+x y y.~1~ numbered: x y y.~1~ y.~2~
+x y y.~1~ t: x y y.~1~ y.~2~
+x y y.~1~ existing: x y y.~1~ y.~2~
+x y y.~1~ nil: x y y.~1~ y.~2~
+x y y.~1~ simple: x y y.~1~ y~
+x y y.~1~ never: x y y.~1~ y~
+x y y~ y.~1~ none: x y y.~1~ y~
+x y y~ y.~1~ off: x y y.~1~ y~
+x y y~ y.~1~ numbered: x y y.~1~ y.~2~ y~
+x y y~ y.~1~ t: x y y.~1~ y.~2~ y~
+x y y~ y.~1~ existing: x y y.~1~ y.~2~ y~
+x y y~ y.~1~ nil: x y y.~1~ y.~2~ y~
+x y y~ y.~1~ simple: x y y.~1~ y~
+x y y~ y.~1~ never: x y y.~1~ y~
+EOF
+
+sed 's/: x/:/' $expected-tmp |cat $expected-tmp - > $expected
+
+exec 1>&3 3>&-
+
+compare $expected $actual || fail=1
+
+Exit $fail
diff --git a/tests/cp/cp-mv-enotsup-xattr.sh b/tests/cp/cp-mv-enotsup-xattr.sh
new file mode 100755
index 0000000..4b967eb
--- /dev/null
+++ b/tests/cp/cp-mv-enotsup-xattr.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+# Ensure that mv, cp -a and cp --preserve=xattr(all) options do work
+# as expected on file systems without their support and do show correct
+# diagnostics when required
+
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp mv
+
+require_root_
+
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/noxattr"; umount "$cwd/xattr"; }
+
+skip=0
+
+# Mount an ext2 loopback file system at $WHERE with $OPTS
+make_fs() {
+ where="$1"
+ opts="$2"
+
+ fs="$where.bin"
+
+ dd if=/dev/zero of="$fs" bs=8192 count=200 > /dev/null 2>&1 \
+ || skip=1
+ mkdir "$where" || skip=1
+ mkfs -t ext2 -F "$fs" ||
+ skip_ "failed to create ext2 file system"
+ mount -oloop,$opts "$fs" "$where" || skip=1
+ echo test > "$where"/f || skip=1
+ test -s "$where"/f || skip=1
+
+ test $skip = 1 &&
+ skip_ "insufficient mount/ext2 support"
+}
+
+make_fs noxattr nouser_xattr
+make_fs xattr user_xattr
+
+# testing xattr name-value pair
+xattr_name="user.foo"
+xattr_value="bar"
+xattr_pair="$xattr_name=\"$xattr_value\""
+
+echo test > xattr/a || framework_failure_
+getfattr -d xattr/a >out_a || skip_ "failed to get xattr of file"
+grep -F "$xattr_pair" out_a >/dev/null && framework_failure_
+setfattr -n "$xattr_name" -v "$xattr_value" xattr/a >out_a \
+ || skip_ "failed to set xattr of file"
+getfattr -d xattr/a >out_a || skip_ "failed to get xattr of file"
+grep -F "$xattr_pair" out_a >/dev/null \
+ || skip_ "failed to set xattr of file"
+
+
+# This should pass without diagnostics
+cp -a xattr/a noxattr/ 2>err || fail=1
+test -s noxattr/a || fail=1 # destination file must not be empty
+compare /dev/null err || fail=1
+
+rm -f err noxattr/a
+
+# This should pass without diagnostics (new file)
+cp --preserve=all xattr/a noxattr/ 2>err || fail=1
+test -s noxattr/a || fail=1 # destination file must not be empty
+compare /dev/null err || fail=1
+
+# This should pass without diagnostics (existing file)
+cp --preserve=all xattr/a noxattr/ 2>err || fail=1
+test -s noxattr/a || fail=1 # destination file must not be empty
+compare /dev/null err || fail=1
+
+rm -f err noxattr/a
+
+# This should fail with corresponding diagnostics
+cp -a --preserve=xattr xattr/a noxattr/ 2>err && fail=1
+if grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null; then
+cat <<\EOF > exp
+cp: setting attributes for 'noxattr/a': Operation not supported
+EOF
+else
+cat <<\EOF > exp
+cp: cannot preserve extended attributes, cp is built without xattr support
+EOF
+fi
+
+compare exp err || fail=1
+
+rm -f err noxattr/a
+
+# This should pass without diagnostics
+mv xattr/a noxattr/ 2>err || fail=1
+test -s noxattr/a || fail=1 # destination file must not be empty
+compare /dev/null err || fail=1
+
+# This should pass and copy xattrs of the symlink
+# since the xattrs used here are not in the 'user.' namespace.
+# Up to and including coreutils-8.22 xattrs of symlinks
+# were not copied across file systems.
+ln -s 'foo' xattr/symlink || framework_failure_
+# Note 'user.' namespace is only supported on regular files/dirs
+# so use the 'trusted.' namespace here
+txattr='trusted.overlay.whiteout'
+if setfattr -hn "$txattr" -v y xattr/symlink; then
+ # Note only root can read the 'trusted.' namespace
+ if getfattr -h -m- -d xattr/symlink | grep -F "$txattr"; then
+ mv xattr/symlink noxattr/ 2>err || fail=1
+ if grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null; then
+ getfattr -h -m- -d noxattr/symlink | grep -F "$txattr" || fail=1
+ fi
+ compare /dev/null err || fail=1
+ else
+ echo "failed to get '$txattr' xattr. skipping symlink check" >&2
+ fi
+else
+ echo "failed to set '$txattr' xattr. skipping symlink check" >&2
+fi
+
+Exit $fail
diff --git a/tests/cp/cp-parents.sh b/tests/cp/cp-parents.sh
new file mode 100755
index 0000000..9437504
--- /dev/null
+++ b/tests/cp/cp-parents.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+working_umask_or_skip_
+
+# Run the setgid check from the just-created directory.
+skip_if_setgid_
+
+{
+ mkdir foo bar
+ mkdir -p a/b/c d e g
+ ln -s d/a sym
+ touch f
+} || framework_failure_
+
+# With 4.0.37 and earlier (back to when?), this would fail
+# with the failed assertion from dirname.c due to the trailing slash.
+cp -R --parents foo/ bar || fail=1
+
+# Exercise the make_path and re_protect code in cp.c.
+# FIXME: compare verbose output with expected output.
+cp --verbose -a --parents a/b/c d > /dev/null 2>&1 || fail=1
+test -d d/a/b/c || fail=1
+
+# With 6.7 and earlier, cp --parents f/g d would mistakenly create a
+# directory d/f, even though f is a regular file.
+returns_ 1 cp --parents f/g d 2>/dev/null || fail=1
+test -d d/f && fail=1
+
+# Check that re_protect works.
+chmod go=w d/a || framework_failure_
+cp -a --parents d/a/b/c e || fail=1
+cp -a --parents sym/b/c g || fail=1
+p=$(ls -ld e/d|cut -b-10); case $p in drwxr-xr-x);; *) fail=1;; esac
+p=$(ls -ld e/d/a|cut -b-10); case $p in drwx-w--w-);; *) fail=1;; esac
+p=$(ls -ld g/sym|cut -b-10); case $p in drwx-w--w-);; *) fail=1;; esac
+p=$(ls -ld e/d/a/b/c|cut -b-10); case $p in drwxr-xr-x);; *) fail=1;; esac
+p=$(ls -ld g/sym/b/c|cut -b-10); case $p in drwxr-xr-x);; *) fail=1;; esac
+
+# Before 8.25 cp --parents --no-preserve=mode would copy
+# the mode bits from the source directories
+{
+ mkdir -p np/b &&
+ chmod 0700 np &&
+ touch np/b/file &&
+ chmod 775 np/b/file &&
+ mkdir np_dest
+} || framework_failure_
+cp --parents --no-preserve=mode np/b/file np_dest/ || fail=1
+p=$(ls -ld np_dest/np|cut -b-10); case $p in drwxr-xr-x);; *) fail=1;; esac
+
+# coreutils 9.1-9.3 inclusive would fail to copy acls for absolute dirs
+mkdir dest || framework_failure_
+if test -f /bin/ls; then
+ cp -t dest --parents -p /bin/ls || fail=1
+fi
+
+Exit $fail
diff --git a/tests/cp/cross-dev-symlink.sh b/tests/cp/cross-dev-symlink.sh
new file mode 100755
index 0000000..21340b4
--- /dev/null
+++ b/tests/cp/cross-dev-symlink.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Ensure symlinks can be replaced across devices
+
+# Copyright (C) 2018-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_root_
+
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/mnt"; }
+
+truncate -s100M fs.img || framework_failure_
+mkfs -t ext4 fs.img || skip_ 'failed to create ext4 file system'
+mkdir mnt || framework_failure_
+mount fs.img mnt || skip_ 'failed to mount ext4 file system'
+
+mkdir mnt/path1 || framework_failure_
+touch mnt/path1/file || framework_failure_
+mkdir path2 || framework_failure_
+cd path2 && ln -s ../mnt/path1/file || framework_failure_
+
+cp -dsf ../mnt/path1/file . || fail=1
+
+Exit $fail
diff --git a/tests/cp/debug.sh b/tests/cp/debug.sh
new file mode 100755
index 0000000..242894d
--- /dev/null
+++ b/tests/cp/debug.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Ensure that cp --debug works as documented
+
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch file || framework_failure_
+cp --debug file file.cp >cp.out || fail=1
+grep 'copy offload:.*reflink:.*sparse detection:' cp.out || fail=1
+cp --debug --attributes-only file file.cp >cp.out || fail=1
+returns_ 1 grep 'copy offload:.*reflink:.*sparse detection:' cp.out || fail=1
+
+touch file.cp || framework_failure_
+cp --debug --update=none file file.cp >cp.out || fail=1
+grep 'skipped' cp.out || fail=1
+
+Exit $fail
diff --git a/tests/cp/deref-slink.sh b/tests/cp/deref-slink.sh
new file mode 100755
index 0000000..a0088c3
--- /dev/null
+++ b/tests/cp/deref-slink.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Demonstrate bug when using -d with an existing destination file
+# that is a symlink.
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch f slink-target || framework_failure_
+ln -s slink-target slink || framework_failure_
+
+cp -d f slink || fail=1
+
+Exit $fail
diff --git a/tests/cp/dir-rm-dest.sh b/tests/cp/dir-rm-dest.sh
new file mode 100755
index 0000000..5a8233d
--- /dev/null
+++ b/tests/cp/dir-rm-dest.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# verify cp's --remove-destination option
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir d e || framework_failure_
+
+# Do it once with no destination...
+cp -R --remove-destination d e || fail=1
+
+# ...and again, with an existing destination.
+cp -R --remove-destination d e || fail=1
+
+# verify no ELOOP which was the case in <= 8.29
+ln -s loop loop || framework_failure_
+touch file || framework_failure_
+cp --remove-destination file loop || fail=1
+
+Exit $fail
diff --git a/tests/cp/dir-slash.sh b/tests/cp/dir-slash.sh
new file mode 100755
index 0000000..d69b802
--- /dev/null
+++ b/tests/cp/dir-slash.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Make sure that cp -R DIR1 DIR2 does the right thing
+# when DIR1 is written with a trailing slash.
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir dir1 dir2 || framework_failure_
+touch dir1/file || framework_failure_
+
+cp -R dir1/ dir2 || fail=1
+
+# This file should not exist, but it did with fileutils-4.0w.
+test -r dir2/file && fail=1
+
+# These two should.
+test -r dir2/dir1/file || fail=1
+test -r dir1/file || fail=1
+
+Exit $fail
diff --git a/tests/cp/dir-vs-file.sh b/tests/cp/dir-vs-file.sh
new file mode 100755
index 0000000..f4ce100
--- /dev/null
+++ b/tests/cp/dir-vs-file.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# A directory may not replace an existing file.
+
+# Copyright (C) 2001-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir dir || framework_failure_
+touch file || framework_failure_
+
+
+# In 4.0.35, this cp invocation silently succeeded.
+returns_ 1 cp -R dir file 2>/dev/null || fail=1
+
+# Make sure file is not replaced with a directory.
+# In 4.0.35, it was.
+test -f file || fail=1
+
+Exit $fail
diff --git a/tests/cp/existing-perm-dir.sh b/tests/cp/existing-perm-dir.sh
new file mode 100755
index 0000000..16d5f6c
--- /dev/null
+++ b/tests/cp/existing-perm-dir.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Make sure cp -p doesn't "restore" permissions it shouldn't (Bug#9170).
+
+# Copyright 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+umask 002
+mkdir -p -m ug-s,u=rwx,g=rwx,o=rx src/dir || fail=1
+mkdir -p -m ug-s,u=rwx,g=,o= dst/dir || fail=1
+
+cp -r src/. dst/ || fail=1
+
+mode=$(stat --p=%A dst/dir)
+test "$mode" = drwx------ || fail=1
+
+Exit $fail
diff --git a/tests/cp/existing-perm-race.sh b/tests/cp/existing-perm-race.sh
new file mode 100755
index 0000000..44641b4
--- /dev/null
+++ b/tests/cp/existing-perm-race.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+# Make sure cp -p isn't too generous with existing file permissions.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_membership_in_two_groups_
+
+# cp -p gives ENOTSUP on NFS on Linux 2.6.9 at least
+require_local_dir_
+
+require_no_default_acl_ .
+
+set _ $groups; shift
+g1=$1
+g2=$2
+
+
+umask 077
+mkfifo_or_skip_ fifo
+
+touch fifo-copy &&
+chgrp $g1 fifo &&
+chgrp $g2 fifo-copy &&
+chmod g+r fifo-copy || framework-failure
+
+# Terminate any background cp process
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+# Copy a fifo's contents. That way, we can examine the
+# destination permissions before they're finalized.
+cp -p --copy-contents fifo fifo-copy & pid=$!
+
+(
+ # Now 'cp' is reading the fifo. Wait for the destination file to
+ # be written to, encouraging things along by echoing to the fifo.
+ while test ! -s fifo-copy; do
+ echo foo
+ done
+
+ # Check the permissions of the destination.
+ ls -l -n fifo-copy >ls.out &&
+
+ # Close the fifo so that "cp" can continue. But output first,
+ # before exiting, otherwise some shells would optimize away the file
+ # descriptor that holds the fifo open.
+ echo foo
+) >fifo || fail=1
+
+# Check that the destination mode is safe while the file is being copied.
+read mode links owner group etc <ls.out || fail=1
+case $mode in
+ -rw-------*) ;;
+
+ # FIXME: Remove the following case; the file mode should always be
+ # 600 while the data are being copied. This will require changing
+ # cp so that it also does not put $g1's data in a file that is
+ # accessible to $g2. This fix will not close a security hole, since
+ # a $g2 process can maintain an open file descriptor to the
+ # destination, but it's safer anyway.
+ -rw-r-----*)
+ # If the file has group $g1 and is group-readable, that is definitely bogus,
+ # as neither the source nor the destination was readable to group $g1.
+ test "$group" = "$g1" && fail=1;;
+
+ *) fail=1;;
+esac
+
+wait $pid || fail=1
+
+# Check that the final mode and group are right.
+ls -l -n fifo-copy >ls.out &&
+read mode links owner group etc <ls.out || fail=1
+case $mode in
+ -rw-------*) test "$group" = "$g1" || fail=1;;
+ *) fail=1;;
+esac
+
+Exit $fail
diff --git a/tests/cp/fail-perm.sh b/tests/cp/fail-perm.sh
new file mode 100755
index 0000000..be32d5d
--- /dev/null
+++ b/tests/cp/fail-perm.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+skip_if_root_
+
+chmod g-s . || framework_failure_
+mkdir D D/D || framework_failure_
+touch D/a || framework_failure_
+chmod 0 D/a || framework_failure_
+chmod u=rx,go=,-st D || framework_failure_
+
+
+# This is expected to exit non-zero, because it can't read D/a.
+returns_ 1 cp -pR D DD > /dev/null 2>&1 || fail=1
+
+# Permissions on DD must be 'dr-x------'
+
+mode=$(ls -ld DD|cut -b-10)
+test "$mode" = dr-x------ || fail=1
+
+chmod 0 D
+ln -s D/D symlink
+touch F
+cat > exp <<\EOF
+cp: cannot stat 'symlink': Permission denied
+EOF
+
+cp F symlink 2> out && fail=1
+# HPUX appears to fail with EACCES rather than EPERM.
+# Transform their diagnostic
+# ...: The file access permissions do not allow the specified action.
+# to the expected one:
+sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
+compare exp out || fail=1
+
+cp --no-target-directory F symlink 2> out && fail=1
+sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
+compare exp out || fail=1
+
+cat > exp <<\EOF
+cp: target directory 'symlink': Permission denied
+EOF
+
+cp --target-directory=symlink F 2> out && fail=1
+sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
+compare exp out || fail=1
+
+chmod 700 D
+
+Exit $fail
diff --git a/tests/cp/file-perm-race.sh b/tests/cp/file-perm-race.sh
new file mode 100755
index 0000000..7036884
--- /dev/null
+++ b/tests/cp/file-perm-race.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+# Make sure cp -p isn't too generous with file permissions.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# cp -p gives ENOTSUP on NFS on Linux 2.6.9 at least
+require_local_dir_
+
+umask 022
+mkfifo_or_skip_ fifo
+
+# Terminate any background cp process
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+# Copy a fifo's contents. That way, we can examine the
+# destination permissions before they're finalized.
+cp -p --copy-contents fifo fifo-copy & pid=$!
+
+(
+ # Now 'cp' is reading the fifo. Wait for the destination file to
+ # be created, encouraging things along by echoing to the fifo.
+ while test ! -f fifo-copy; do
+ echo foo
+ done
+
+ # Check the permissions of the destination.
+ ls -l fifo-copy >ls.out
+
+ # Close the fifo so that "cp" can continue. But output first,
+ # before exiting, otherwise some shells would optimize away the file
+ # descriptor that holds the fifo open.
+ echo foo
+) >fifo
+
+case $(cat ls.out) in
+-???------*) ;;
+*) fail=1;;
+esac
+
+wait $pid || fail=1
+
+Exit $fail
diff --git a/tests/cp/into-self.sh b/tests/cp/into-self.sh
new file mode 100755
index 0000000..fb2e820
--- /dev/null
+++ b/tests/cp/into-self.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# Confirm that copying a directory into itself gets a proper diagnostic.
+
+# Copyright (C) 2001-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# In 4.0.35 and earlier, 'mkdir dir && cp -R dir dir' would produce this:
+# cp: won't create hard link 'dir/dir/dir' to directory ''
+# Now it gives this:
+# cp: can't copy a directory 'dir' into itself 'dir/dir'
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir a dir || framework_failure_
+
+
+# This command should exit nonzero.
+cp -R dir dir 2> out && fail=1
+echo 1 >> out
+
+# This should, too. However, with coreutils-7.1 it would infloop.
+cp -rl dir dir 2>> out && fail=1
+echo 2 >> out
+
+cp -rl a dir dir 2>> out && fail=1
+echo 3 >> out
+cp -rl a dir dir 2>> out && fail=1
+echo 4 >> out
+
+cat > exp <<\EOF
+cp: cannot copy a directory, 'dir', into itself, 'dir/dir'
+1
+cp: cannot copy a directory, 'dir', into itself, 'dir/dir'
+2
+cp: cannot copy a directory, 'dir', into itself, 'dir/dir'
+3
+cp: cannot copy a directory, 'dir', into itself, 'dir/dir'
+4
+EOF
+#'
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/cp/link-deref.sh b/tests/cp/link-deref.sh
new file mode 100755
index 0000000..28b20a2
--- /dev/null
+++ b/tests/cp/link-deref.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+# Exercise cp --link's behavior regarding the dereferencing of symbolic links.
+
+# Copyright (C) 2013-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+if { grep '^#define HAVE_LINKAT 1' "$CONFIG_HEADER" > /dev/null \
+ && grep '#undef LINKAT_SYMLINK_NOTSUP' "$CONFIG_HEADER" > /dev/null; } \
+ || grep '^#define LINK_FOLLOWS_SYMLINKS 0' "$CONFIG_HEADER" > /dev/null; then
+ # With this config cp will attempt to linkat() to hardlink a symlink.
+ # So now double check the current file system supports this operation.
+ ln -s testtarget test_sl || framework_failure_
+ ln -P test_sl test_hl_sl || framework_failure_
+ ino_sl="$(stat -c '%i' test_sl)" || framework_failure_
+ ino_hl="$(stat -c '%i' test_hl_sl)" || framework_failure_
+ test "$ino_sl" = "$ino_hl" && can_hardlink_to_symlink=1
+fi
+
+mkdir dir || framework_failure_
+> file || framework_failure_
+ln -s dir dirlink || framework_failure_
+ln -s file filelink || framework_failure_
+ln -s nowhere danglink || framework_failure_
+
+# printf format of the output line.
+outformat='%s|result=%s|inode=%s|type=%s|error=%s\n'
+
+for src in dirlink filelink danglink; do
+ # Get symlink's target.
+ tgt=$(readlink $src) || framework_failure_
+ # Get inodes and file type of the symlink (src) and its target (tgt).
+ # Note: this will fail for 'danglink'; catch it.
+ ino_src="$(stat -c '%i' $src)" || framework_failure_
+ typ_src="$(stat -c '%F' $src)" || framework_failure_
+ ino_tgt="$(stat -c '%i' $tgt 2>/dev/null)" || ino_tgt=
+ typ_tgt="$(stat -c '%F' $tgt 2>/dev/null)" || typ_tgt=
+
+ for o in '' -L -H -P; do
+
+ # Skip the -P case where we don't or can't hardlink symlinks
+ ! test "$can_hardlink_to_symlink" && test "$o" = '-P' && continue
+
+ for r in '' -R; do
+
+ command="cp --link $o $r $src dst"
+ $command 2> err
+ result=$?
+
+ # Get inode and file type of the destination (which may fail, too).
+ ino_dst="$(stat -c '%i' dst 2>/dev/null)" || ini_dst=
+ typ_dst="$(stat -c '%F' dst 2>/dev/null)" || typ_dst=
+
+ # Print the actual result in a certain format.
+ printf "$outformat" \
+ "$command" \
+ "$result" \
+ "$ino_dst" \
+ "$typ_dst" \
+ "$(cat err)" \
+ > out
+
+ # What was expected?
+ if [ "$o" = "-P" ]; then
+ # cp --link should not dereference if -P is given.
+ exp_result=0
+ exp_inode=$ino_src
+ exp_ftype=$typ_src
+ exp_error=
+ elif [ "$src" = 'danglink' ]; then
+ # Dereferencing should fail for the 'danglink'.
+ exp_result=1
+ exp_inode=
+ exp_ftype=
+ exp_error="cp: cannot stat 'danglink': No such file or directory"
+ elif [ "$src" = 'dirlink' ] && [ "$r" != '-R' ]; then
+ # Dereferencing should fail for the 'dirlink' without -R.
+ exp_result=1
+ exp_inode=
+ exp_ftype=
+ exp_error="cp: -r not specified; omitting directory 'dirlink'"
+ elif [ "$src" = 'dirlink' ]; then
+ # cp --link -R 'dirlink' should create a new directory.
+ exp_result=0
+ exp_inode=$ino_dst
+ exp_ftype=$typ_dst
+ exp_error=
+ else
+ # cp --link 'filelink' should create a hard link to the target.
+ exp_result=0
+ exp_inode=$ino_tgt
+ exp_ftype=$typ_tgt
+ exp_error=
+ fi
+
+ # Print the expected result in a certain format.
+ printf "$outformat" \
+ "$command" \
+ "$exp_result" \
+ "$exp_inode" \
+ "$exp_ftype" \
+ "$exp_error" \
+ > exp
+
+ compare exp out || { ls -lid $src $tgt dst; fail=1; }
+
+ rm -rf dst err exp out || framework_failure_
+ done
+ done
+done
+
+Exit $fail
diff --git a/tests/cp/link-heap.sh b/tests/cp/link-heap.sh
new file mode 100755
index 0000000..42ac9a9
--- /dev/null
+++ b/tests/cp/link-heap.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# ensure that cp --preserve=link --link doesn't waste heap
+
+# Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+expensive_
+
+# Determine basic amount of memory needed for 'cp -al'.
+touch f || framework_failure_
+vm=$(get_min_ulimit_v_ cp -al f f2) \
+ || skip_ "this shell lacks ulimit support"
+rm f f2 || framework_failure_
+
+a=$(printf %031d 0)
+b=$(printf %031d 1)
+(mkdir $a \
+ && cd $a \
+ && seq --format=%031g 10000 |xargs touch \
+ && seq --format=d%030g 10000 |xargs mkdir ) || framework_failure_
+cp -al $a $b || framework_failure_
+mkdir e || framework_failure_
+mv $a $b e || framework_failure_
+
+# Allow cp(1) to use 4MiB more virtual memory than for the above trivial case.
+(ulimit -v $(($vm+4000)) && cp -al e f) || fail=1
+
+Exit $fail
diff --git a/tests/cp/link-no-deref.sh b/tests/cp/link-no-deref.sh
new file mode 100755
index 0000000..2c60ef6
--- /dev/null
+++ b/tests/cp/link-no-deref.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Ensure that cp --link --no-dereference works properly
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+ln -s no-such-file dangling-slink || framework_failure_
+
+
+# Prior to coreutils-6.0, this would fail on non-Linux kernels,
+# with link being applied to the dangling symlink.
+cp --link --no-dereference dangling-slink d2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/link-preserve.sh b/tests/cp/link-preserve.sh
new file mode 100755
index 0000000..c938a94
--- /dev/null
+++ b/tests/cp/link-preserve.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+# ensure that 'cp -d' preserves hard-links between command line arguments
+# ensure that --preserve=links works with -RH and -RL
+
+# Copyright (C) 2001-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch a || framework_failure_
+ln a b || framework_failure_
+mkdir c || framework_failure_
+cp -d a b c || framework_failure_
+test -f c/a || framework_failure_
+test -f c/b || framework_failure_
+
+
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" || fail=1
+# --------------------------------------
+
+rm -rf a b c
+touch a
+ln -s a b
+mkdir c
+cp --preserve=links -R -H a b c || fail=1
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" || fail=1
+# --------------------------------------
+
+# Ensure that -L makes cp follow the b->a symlink
+# and translates to hard-linked a and b in the destination dir.
+rm -rf a b c d; mkdir d; (cd d; touch a; ln -s a b)
+cp --preserve=links -R -L d c || fail=1
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" || fail=1
+# --------------------------------------
+
+# Same as above, but starting with a/b hard linked.
+rm -rf a b c d; mkdir d; (cd d; touch a; ln a b)
+cp --preserve=links -R -L d c || fail=1
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" || fail=1
+# --------------------------------------
+
+# Ensure that --no-preserve=links works.
+rm -rf a b c d; mkdir d; (cd d; touch a; ln a b)
+cp -dR --no-preserve=links d c || fail=1
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" && fail=1
+# --------------------------------------
+
+# Ensure that -d still preserves hard links.
+rm -rf a b c d
+touch a; ln a b
+mkdir c
+cp -d a b c || fail=1
+a_inode=$(ls -i c/a|sed 's,c/.*,,')
+b_inode=$(ls -i c/b|sed 's,c/.*,,')
+test "$a_inode" = "$b_inode" || fail=1
+# --------------------------------------
+
+# Ensure that --no-preserve=mode works
+rm -rf a b c d
+touch a; chmod 731 a
+umask 077
+cp -a --no-preserve=mode a b || fail=1
+mode=$(ls -l b|cut -b-10)
+test "$mode" = "-rw-------" || fail=1
+umask 022
+# --------------------------------------
+
+Exit $fail
diff --git a/tests/cp/link-symlink.sh b/tests/cp/link-symlink.sh
new file mode 100755
index 0000000..8df5384
--- /dev/null
+++ b/tests/cp/link-symlink.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Ensure that cp -a --link maintains timestamps if possible
+
+# Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# Check that the timestamps of the symlink are copied
+# if we're using hardlink to symlink emulation.
+touch file
+ln -s file link || framework_failure_
+touch -m -h -d 2011-01-01 link ||
+ skip_ "Your system doesn't support updating symlink timestamps"
+case $(stat --format=%y link) in
+ 2011-01-01*) ;;
+ *) skip_ "Your system doesn't support updating symlink timestamps" ;;
+esac
+
+# link.cp is probably a hardlink, but may also be a symlink
+# In either case the timestamp should match the original.
+cp -al link link.cp || fail=1
+case $(stat --format=%y link.cp) in
+ 2011-01-01*) ;;
+ *) fail=1 ;;
+esac
+
+Exit $fail
diff --git a/tests/cp/link.sh b/tests/cp/link.sh
new file mode 100755
index 0000000..e560154
--- /dev/null
+++ b/tests/cp/link.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Make sure cp --link -f works when the target exists.
+# This failed for 4.0z (due to a bug introduced in that test release).
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch src || framework_failure_
+touch dest || framework_failure_
+touch dest2 || framework_failure_
+
+
+cp -f --link src dest || fail=1
+cp -f --symbolic-link src dest2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/nfs-removal-race.sh b/tests/cp/nfs-removal-race.sh
new file mode 100755
index 0000000..679397e
--- /dev/null
+++ b/tests/cp/nfs-removal-race.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# Running cp S D on an NFS client while another client has just removed D
+# would lead (w/coreutils-8.16 and earlier) to cp's initial stat call
+# seeing (via stale NFS cache) that D exists, so that cp would then call
+# open without the O_CREAT flag. Yet, the open must actually consult
+# the server, which confesses that D has been deleted, thus causing the
+# open call to fail with ENOENT.
+#
+# This test simulates that situation by intercepting stat for a nonexistent
+# destination, D, and making the stat fill in the result struct for another
+# file and return 0.
+#
+# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
+# Similarly, on a system that lacks <dlfcn.h> or __xstat, skipping it is fine.
+
+# Copyright (C) 2012-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_gcc_shared_
+
+# Replace each stat call with a call to this wrapper.
+cat > k.c <<'EOF' || framework_failure_
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+#include <dlfcn.h>
+
+#define __xstat __xstat_orig
+
+#include <sys/stat.h>
+#include <stddef.h>
+
+#undef __xstat
+
+int
+__xstat (int ver, const char *path, struct stat *st)
+{
+ static int (*real_stat)(int ver, const char *path, struct stat *st) = NULL;
+ fclose(fopen("preloaded", "w"));
+ if (!real_stat)
+ real_stat = dlsym (RTLD_NEXT, "__xstat");
+ /* When asked to stat nonexistent "d",
+ return results suggesting it exists. */
+ return real_stat (ver, *path == 'd' && path[1] == 0 ? "d2" : path, st);
+}
+EOF
+
+# Then compile/link it:
+gcc_shared_ k.c k.so \
+ || framework_failure_ 'failed to build shared library'
+
+touch d2 || framework_failure_
+echo xyz > src || framework_failure_
+
+# Finally, run the test:
+LD_PRELOAD=$LD_PRELOAD:./k.so cp src d || fail=1
+
+test -f preloaded || skip_ 'LD_PRELOAD was ineffective?'
+
+compare src d || fail=1
+Exit $fail
diff --git a/tests/cp/no-ctx.sh b/tests/cp/no-ctx.sh
new file mode 100755
index 0000000..8c69bf3
--- /dev/null
+++ b/tests/cp/no-ctx.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Ensure we handle file systems returning no SELinux context,
+# which triggered a segmentation fault in coreutils-8.22.
+# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
+# Similarly, on a system that lacks lgetfilecon altogether, skipping it is fine.
+
+# Copyright (C) 2014-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_gcc_shared_
+require_selinux_
+
+# Replace each getfilecon and lgetfilecon call with a call to these stubs.
+cat > k.c <<'EOF' || framework_failure_
+#include <stdio.h>
+#include <selinux/selinux.h>
+#include <errno.h>
+
+int getfilecon (const char *path, char **con)
+{
+ /* Leave a marker so we can identify if the function was intercepted. */
+ fclose(fopen("preloaded", "w"));
+
+ errno=ENODATA;
+ return -1;
+}
+
+int lgetfilecon (const char *path, char **con)
+{ return getfilecon (path, con); }
+EOF
+
+# Then compile/link it:
+gcc_shared_ k.c k.so \
+ || skip_ 'failed to build SELinux shared library'
+
+touch file_src
+
+# New file with SELinux context optionally included
+LD_PRELOAD=$LD_PRELOAD:./k.so cp -a file_src file_dst || fail=1
+
+# Existing file with SELinux context optionally included
+LD_PRELOAD=$LD_PRELOAD:./k.so cp -a file_src file_dst || fail=1
+
+# ENODATA should give an immediate error when required to preserve ctx
+# This is debatable, and maybe we should not fail when no context available?
+( export LD_PRELOAD=$LD_PRELOAD:./k.so
+ returns_ 1 cp --preserve=context file_src file_dst ) || fail=1
+
+test -e preloaded || skip_ 'LD_PRELOAD interception failed'
+
+Exit $fail
diff --git a/tests/cp/no-deref-link1.sh b/tests/cp/no-deref-link1.sh
new file mode 100755
index 0000000..3b3d992
--- /dev/null
+++ b/tests/cp/no-deref-link1.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# cp from 3.16 fails this test
+
+# Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir a b
+msg=bar
+echo $msg > a/foo
+cd b
+ln -s ../a/foo .
+cd ..
+
+
+# It should fail with a message something like this:
+# ./cp: 'a/foo' and 'b/foo' are the same file
+# Fail this test if the exit status is not 1
+returns_ 1 cp -d a/foo b 2>/dev/null || fail=1
+
+test "$(cat a/foo)" = $msg || fail=1
+
+Exit $fail
diff --git a/tests/cp/no-deref-link2.sh b/tests/cp/no-deref-link2.sh
new file mode 100755
index 0000000..6eb47a8
--- /dev/null
+++ b/tests/cp/no-deref-link2.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# cp from 3.16 fails this test
+
+# Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir b
+msg=bar
+echo $msg > a
+cd b
+ln -s ../a .
+cd ..
+
+
+# It should fail with a message something like this:
+# cp: 'a' and 'b/foo' are the same file
+# Fail this test if the exit status is not 1
+returns_ 1 cp -d a b 2>/dev/null || fail=1
+
+test "$(cat a)" = $msg || fail=1
+
+Exit $fail
diff --git a/tests/cp/no-deref-link3.sh b/tests/cp/no-deref-link3.sh
new file mode 100755
index 0000000..1d7d704
--- /dev/null
+++ b/tests/cp/no-deref-link3.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# cp from 3.16 fails this test
+
+# Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+msg=bar
+echo $msg > a
+ln -s a b
+
+
+# It should fail with a message something like this:
+# cp: 'a' and 'b' are the same file
+# Fail this test if the exit status is not 1
+returns_ 1 cp -d a b 2>/dev/null || fail=1
+
+test "$(cat a)" = $msg || fail=1
+
+Exit $fail
diff --git a/tests/cp/parent-perm-race.sh b/tests/cp/parent-perm-race.sh
new file mode 100755
index 0000000..694ffd9
--- /dev/null
+++ b/tests/cp/parent-perm-race.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Make sure cp -pR --parents isn't too generous with parent permissions.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# cp -p gives ENOTSUP on NFS on Linux 2.6.9 at least
+require_local_dir_
+
+umask 002
+mkdir mode ownership d || framework_failure_
+chmod g+s d 2>/dev/null # The cp test is valid either way.
+
+# Terminate any background cp process.
+pid=
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+for attr in mode ownership
+do
+ mkfifo_or_skip_ $attr/fifo
+
+ # Copy a fifo's contents. That way, we can examine d/$attr's
+ # state while cp is running.
+ timeout 10 cp --preserve=$attr -R --copy-contents --parents $attr d & pid=$!
+
+ # Check the permissions of the destination directory that 'cp' has made.
+ # 'ls' won't start until after 'cp' has made the destination directory
+ # $d/attr and has started to read the source file $attr/fifo.
+ timeout 10 sh -c "ls -ld d/$attr >$attr/fifo" || fail=1
+
+ wait $pid || fail=1
+
+ ls_output=$(cat d/$attr/fifo) || fail=1
+ case $attr,$ls_output in
+ ownership,d???--[-S]--[-S]* | \
+ mode,d????-??-?* | \
+ mode,d??[-x]?w[-x]?-[-x]* )
+ ;;
+ *)
+ fail=1;;
+ esac
+done
+
+Exit $fail
diff --git a/tests/cp/parent-perm.sh b/tests/cp/parent-perm.sh
new file mode 100755
index 0000000..a79164e
--- /dev/null
+++ b/tests/cp/parent-perm.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+# Ensure that cp --parents works properly with a preexisting dest. directory
+
+# Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+working_umask_or_skip_
+# cp -p gives ENOTSUP on NFS on Linux 2.6.9 at least
+require_local_dir_
+
+mkdir -p a/b/c a/b/d e || framework_failure_
+touch a/b/c/foo a/b/d/foo || framework_failure_
+cp -p --parent a/b/c/foo e || framework_failure_
+
+# Make permissions of e/a different, so that we exercise the
+# code in cp -p --parents that propagates permissions even
+# to a destination directory that it doesn't create.
+chmod g-rx e/a e/a/b || framework_failure_
+
+cp -p --parent a/b/d/foo e || fail=1
+
+# Ensure that permissions on just-created directory, e/a/,
+# are the same as those on original, a/.
+
+# The sed filter maps any 's' from an inherited set-GID bit
+# to the usual 'x'. Otherwise, under unusual circumstances, this
+# test would fail with e.g., drwxr-sr-x != drwxr-xr-x .
+# For reference, the unusual circumstances is: build dir is set-gid,
+# so "a/" inherits that. However, when the user does not belong to
+# the group of the build directory, chmod ("a/e", 02755) returns 0,
+# yet fails to set the S_ISGID bit.
+for dir in a a/b a/b/d; do
+ test $(stat --printf %A $dir|sed s/s/x/g) \
+ = $(stat --printf %A e/$dir|sed s/s/x/g) ||
+ fail=1
+done
+
+Exit $fail
diff --git a/tests/cp/perm.sh b/tests/cp/perm.sh
new file mode 100755
index 0000000..c6414aa
--- /dev/null
+++ b/tests/cp/perm.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Make sure the permission-preserving code in copy.c (mv, cp, install) works.
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp mv
+
+very_expensive_
+
+umask 037
+
+
+# Now, try it with 'mv', with combinations of --force, no-f and
+# existing-destination and not.
+for u in 31 37 2; do
+ echo umask: $u
+ umask $u
+ for cmd in mv 'cp -p' cp; do
+ for force in '' -f; do
+ for existing_dest in yes no; do
+ for g_perm in r w x rw wx xr rwx; do
+ for o_perm in r w x rw wx xr rwx; do
+ touch src || exit 1
+ chmod u=r,g=rx,o= src || exit 1
+ expected_perms=$(stat --format=%A src)
+ rm -f dest
+ test $existing_dest = yes && {
+ touch dest || exit 1
+ chmod u=rw,g=$g_perm,o=$o_perm dest || exit 1
+ }
+ $cmd $force src dest || exit 1
+ test "$cmd" = mv && test -f src && exit 1
+ test "$cmd" = cp && { test -f src || exit 1; }
+ actual_perms=$(stat --format=%A dest)
+
+ case "$cmd:$force:$existing_dest" in
+ cp:*:yes)
+ _g_perm=$(echo rwx|sed 's/[^'$g_perm']/-/g')
+ _o_perm=$(echo rwx|sed 's/[^'$o_perm']/-/g')
+ expected_perms=-rw-$_g_perm$_o_perm
+ ;;
+ cp:*:no)
+ test $u = 37 &&
+ expected_perms=$(
+ echo $expected_perms | sed 's/.....$/-----/'
+ )
+ test $u = 31 &&
+ expected_perms=$(
+ echo $expected_perms | sed 's/..\(..\).$/--\1-/'
+ )
+ ;;
+ esac
+ test _$actual_perms = _$expected_perms || exit 1
+ # Perform only one iteration when there's no existing destination.
+ test $existing_dest = no && break 3
+ done
+ done
+ done
+ done
+ done
+done
+
+Exit $fail
diff --git a/tests/cp/preserve-2.sh b/tests/cp/preserve-2.sh
new file mode 100755
index 0000000..ed1a9c1
--- /dev/null
+++ b/tests/cp/preserve-2.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# ensure that cp's --preserve=X,Y option is parsed properly
+
+# Copyright (C) 2002-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# cp -p gives ENOTSUP on NFS on Linux 2.6.9 at least
+require_local_dir_
+
+touch f || framework_failure_
+
+cp --preserve=mode,links f g || fail=1
+
+Exit $fail
diff --git a/tests/cp/preserve-gid.sh b/tests/cp/preserve-gid.sh
new file mode 100755
index 0000000..4add028
--- /dev/null
+++ b/tests/cp/preserve-gid.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+# Verify that cp -p preserves GID when it is possible.
+
+# Copyright (C) 2007-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_perl_
+require_root_
+
+# Some of the tests expect a umask that grants group and/or world read access.
+working_umask_or_skip_
+
+# Record primary group number, usually 0.
+# This is the group ID used when cp (without -p) creates a new file.
+primary_group_num=$(id -g)
+
+create() {
+ echo "$1" > "$1" || exit 1
+ chown "+$2:+$3" "$1" || exit 1
+}
+
+t0() {
+ f=$1; shift
+ u=$1; shift
+ g=$1; shift
+ rm -f b || exit 1
+ "$@" "$f" b || exit 1
+ s=$(stat -c '%u %g' b)
+ if test "x$s" != "x$u $g"; then
+ # Allow the actual group to match that of the parent directory
+ # (it was set to 0 above).
+ if test "x$s" = "x$u $primary_group_num"; then
+ :
+ else
+ echo "$0: $* $f b: $u $g != $s" 1>&2
+ Exit 1
+ fi
+ fi
+}
+
+nameless_uid=$($PERL -le '
+ foreach my $i (1000..16*1024-1)
+ {
+ getpwuid $i or (print $i), exit
+ }
+')
+nameless_gid1=$($PERL -le '
+ foreach my $i (1000..16*1024)
+ {
+ getgrgid $i or (print $i), exit
+ }
+')
+nameless_gid2=$($PERL -le '
+ foreach my $i ('"$nameless_gid1"'+1..16*1024)
+ {
+ getgrgid $i or (print $i), exit
+ }
+')
+
+if test -z "$nameless_uid" \
+ || test -z "$nameless_gid1" \
+ || test -z "$nameless_gid2"; then
+ skip_ "couldn't find a nameless UID or GID"
+fi
+
+chown "+$nameless_uid:+0" .
+
+create a0 0 0
+create b0 "$nameless_uid" "$nameless_gid1"
+create b1 "$nameless_uid" "$nameless_gid2"
+create c0 0 "$nameless_gid1"
+create c1 0 "$nameless_gid2"
+
+t0 a0 0 0 cp
+t0 b0 0 0 cp
+t0 b1 0 0 cp
+t0 c0 0 0 cp
+t0 c1 0 0 cp
+
+t0 a0 0 0 cp -p
+t0 b0 "$nameless_uid" "$nameless_gid1" cp -p
+t0 b1 "$nameless_uid" "$nameless_gid2" cp -p
+t0 c0 0 "$nameless_gid1" cp -p
+t0 c1 0 "$nameless_gid2" cp -p
+
+# For the remaining tests, we need a cp binary that is accessible to a user
+# with UID of $nameless_uid. The build directory may not be accessible,
+# so create a temporary directory and copy cp into it, ensure that
+# $nameless_uid can access it and then make that directory the search path.
+tmp_path=
+cleanup_() { rm -rf "$tmp_path"; }
+
+# Cause mktemp to create a directory directly under /tmp.
+# Setting TMPDIR explicitly is required here, in case $TMPDIR
+# is not readable by our nameless IDs.
+test -d /tmp && TMPDIR=/tmp
+tmp_path=$(mktemp -d) || fail_ "failed to create temporary directory"
+if test -x "$abs_path_dir_/coreutils" &&
+ { test -L "$abs_path_dir_/cp" ||
+ test $(wc -l < "$abs_path_dir_/cp") = 1; } then
+ # if configured with --enable-single-binary we need to use the single binary
+ cp "$abs_path_dir_/coreutils" "$tmp_path/cp" || framework_failure_
+else
+ cp "$abs_path_dir_/cp" "$tmp_path"
+fi
+chmod -R a+rx "$tmp_path"
+
+t1() {
+ f=$1; shift
+ u=$1; shift
+ g=$1; shift
+ t0 "$f" "$u" "$g" \
+ chroot --skip-chdir \
+ --user=+$nameless_uid:+$nameless_gid1 \
+ --groups="+$nameless_gid1,+$nameless_gid2" \
+ / env PATH="$tmp_path" "$@"
+}
+
+t1 a0 "$nameless_uid" "$nameless_gid1" cp
+t1 b0 "$nameless_uid" "$nameless_gid1" cp
+t1 b1 "$nameless_uid" "$nameless_gid1" cp
+t1 c0 "$nameless_uid" "$nameless_gid1" cp
+t1 c1 "$nameless_uid" "$nameless_gid1" cp
+
+t1 a0 "$nameless_uid" "$nameless_gid1" cp -p
+t1 b0 "$nameless_uid" "$nameless_gid1" cp -p
+t1 b1 "$nameless_uid" "$nameless_gid2" cp -p
+t1 c0 "$nameless_uid" "$nameless_gid1" cp -p
+t1 c1 "$nameless_uid" "$nameless_gid2" cp -p
+
+Exit $fail
diff --git a/tests/cp/preserve-link.sh b/tests/cp/preserve-link.sh
new file mode 100755
index 0000000..83b3467
--- /dev/null
+++ b/tests/cp/preserve-link.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+# Exercise the fix for https://bugs.gnu.org/8419
+
+# Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+same_inode()
+{
+ local u v
+ u=$(stat --format %i "$1") &&
+ v=$(stat --format %i "$2") && test "$u" = "$v"
+}
+
+create_source_tree()
+{
+ rm -Rf s
+ mkdir s || framework_failure_
+
+ # a missing link in dest will be created
+ touch s/f || framework_failure_
+ ln s/f s/linkm || framework_failure_
+
+ # an existing link in dest will be maintained
+ ln s/f s/linke || framework_failure_
+
+ # a separate older file in dest will be overwritten
+ ln s/f s/fileo || framework_failure_
+
+ # a separate newer file in dest will be overwritten!
+ ln s/f s/fileu || framework_failure_
+}
+
+create_target_tree()
+{
+ f=$1 # which of f or linkm to create in t/
+
+ rm -Rf t
+ mkdir -p t/s/ || framework_failure_
+
+ # a missing link in dest must be created
+ touch t/s/$f || framework_failure_
+
+ # an existing link must be maintained
+ ln t/s/$f t/s/linke || framework_failure_
+
+ # a separate older file in dest will be overwritten
+ touch -d '-1 hour' t/s/fileo || framework_failure_
+
+ # a separate newer file in dest will be overwritten!
+ touch -d '+1 hour' t/s/fileu || framework_failure_
+}
+
+
+# Note we repeat this, creating either one of
+# two hard linked files from source in the dest, so as to
+# test both paths in $(cp) for creating the hard links.
+# The path taken by cp is dependent on which cp encounters
+# first in the source, which is non deterministic currently
+# (I'm guessing that results are sorted by inode and
+# because they're the same here, and due to the sort
+# being unstable, either can be processed first).
+create_source_tree
+
+for f in f linkm; do
+ create_target_tree $f
+
+ # Copy all the hard links across. With cp from coreutils-8.12
+ # and prior, it would sometimes mistakenly copy rather than link.
+ cp -au s t || fail=1
+
+ same_inode t/s/f t/s/linkm || fail=1
+ same_inode t/s/f t/s/linke || fail=1
+ same_inode t/s/f t/s/fileo || fail=1
+ same_inode t/s/f t/s/fileu || fail=1
+done
+
+Exit $fail
diff --git a/tests/cp/preserve-mode.sh b/tests/cp/preserve-mode.sh
new file mode 100755
index 0000000..d27801c
--- /dev/null
+++ b/tests/cp/preserve-mode.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+# Check whether cp generates files with correct modes.
+
+# Copyright (C) 2002-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+get_mode() { stat -c%f "$1"; }
+
+umask 0022 || framework_failure_
+
+#regular file test
+touch a b || framework_failure_
+chmod 600 b || framework_failure_
+cp --no-preserve=mode b c || fail=1
+test "$(get_mode a)" = "$(get_mode c)" || fail=1
+
+#existing destination test
+chmod 600 c || framework_failure_
+cp --no-preserve=mode a b || fail=1
+test "$(get_mode b)" = "$(get_mode c)" || fail=1
+
+#directory test
+mkdir d1 d2 || framework_failure_
+chmod 705 d2 || framework_failure_
+cp --no-preserve=mode -r d2 d3 || fail=1
+test "$(get_mode d1)" = "$(get_mode d3)" || fail=1
+
+#contradicting options test
+rm -f a b || framework_failure_
+touch a || framework_failure_
+chmod 600 a || framework_failure_
+cp --no-preserve=mode --preserve=all a b || fail=1
+test "$(get_mode a)" = "$(get_mode b)" || fail=1
+
+#fifo test
+if mkfifo fifo; then
+ cp -a --no-preserve=mode fifo fifo_copy || fail=1
+ #ensure default perms set appropriate for non regular files
+ #which wasn't done between v8.20 and 8.29 inclusive
+ test "$(get_mode fifo)" = "$(get_mode fifo_copy)" || fail=1
+fi
+
+# Test that plain --preserve=ownership does not affect destination mode.
+rm -f a b c || framework_failure_
+touch a || framework_failure_
+chmod 660 a || framework_failure_
+cp a b || fail=1
+cp --preserve=ownership a c || fail=1
+test "$(get_mode b)" = "$(get_mode c)" || fail=1
+
+Exit $fail
diff --git a/tests/cp/preserve-slink-time.sh b/tests/cp/preserve-slink-time.sh
new file mode 100755
index 0000000..e689f0d
--- /dev/null
+++ b/tests/cp/preserve-slink-time.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Verify that cp -Pp preserves times even on symlinks.
+
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+grep '^#define HAVE_UTIMENSAT 1' "$CONFIG_HEADER" > /dev/null ||
+grep '^#define HAVE_LUTIMES 1' "$CONFIG_HEADER" > /dev/null ||
+ skip_ 'this system lacks the utimensat function'
+
+ln -s no-such dangle || framework_failure_
+
+# If the current file system lacks sub-second resolution, sleep for 2s to
+# ensure that the times on the copy are different from those of the original.
+case $(stat --format=%y dangle) in
+ ??:??:??.000000000) sleep 2;;
+esac
+
+copy_timestamp_() {
+ sleep $1
+ rm -f d2
+ cp -Pp dangle d2 || framework_failure_
+ # Can't use --format=%x, as lstat() modifies atime on some platforms.
+ stat --format=%y dangle > t1 || framework_failure_
+ stat --format=%y d2 > t2 || framework_failure_
+ compare t1 t2
+}
+
+# We retry with a delay at least 1.5s because on GPFS
+# it was seen that the timestamp wasn't updated unless there
+# was sufficient delay between the ln and cp.
+# I.e., if there wasn't sufficient difference in
+# the timestamp being updated, the update was discarded.
+retry_delay_ copy_timestamp_ .1 4 || fail=1
+
+Exit $fail
diff --git a/tests/cp/proc-short-read.sh b/tests/cp/proc-short-read.sh
new file mode 100755
index 0000000..806fe28
--- /dev/null
+++ b/tests/cp/proc-short-read.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# exercise cp's short-read failure when operating on >4KB files in /proc
+
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+proc_large=/proc/cpuinfo # usually > 4KiB
+
+test -r $proc_large || skip_ "your system lacks $proc_large"
+
+# Before coreutils-7.3, cp would copy less than 4KiB of this file.
+# Skip this test when run under QEmu emulation where emulated /proc files
+# have unstable inode numbers.
+cp $proc_large 1 2>err \
+ || { fail=1
+ grep 'replaced while being copied' err \
+ && skip_ "File $proc_large is being replaced while being copied"; }
+
+cat $proc_large > 2 || fail=1
+
+# adjust varying parts
+del_varying='/MHz/d; /[Bb][Oo][Gg][Oo][Mm][Ii][Pp][Ss]/d;'
+sed "$del_varying" 1 > proc.cp || framework_failure_
+sed "$del_varying" 2 > proc.cat || framework_failure_
+
+compare proc.cp proc.cat || fail=1
+
+Exit $fail
diff --git a/tests/cp/proc-zero-len.sh b/tests/cp/proc-zero-len.sh
new file mode 100755
index 0000000..243fb40
--- /dev/null
+++ b/tests/cp/proc-zero-len.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Ensure that cp copies contents of non-empty "regular" file with st_size==0
+
+# Copyright (C) 2007-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch empty || framework_failure_
+
+f=/proc/cpuinfo
+test -r $f || f=empty
+
+cat $f > out || fail=1
+
+# With coreutils-6.9, this would create a zero-length "exp" file.
+# Skip this test on architectures like aarch64 where the inode
+# number of the file changed during the cp run.
+cp $f exp 2>err \
+ || { fail=1;
+ grep 'replaced while being copied' err \
+ && skip_ "File $f is being replaced while being copied"; }
+
+# Don't simply compare contents; they might differ,
+# e.g., if CPU freq changes between cat and cp invocations.
+# Instead, simply compare whether they're both nonempty.
+test -s out \
+ && { rm -f out; echo nonempty > out; }
+test -s exp \
+ && { rm -f exp; echo nonempty > exp; }
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/cp/r-vs-symlink.sh b/tests/cp/r-vs-symlink.sh
new file mode 100755
index 0000000..64b23dc
--- /dev/null
+++ b/tests/cp/r-vs-symlink.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# cp -r should not create symlinks. Fixed in fileutils-4.1.5.
+
+# Copyright (C) 2001-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# Restored old behavior (whereby cp -r preserves symlinks) in 4.1.6,
+# though now such usage evokes a warning:
+# cp: 'slink': WARNING: using -r to copy symbolic links is not portable
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+echo abc > foo || framework_failure_
+ln -s foo slink || framework_failure_
+ln -s no-such-file no-file || framework_failure_
+
+
+# This would fail in 4.1.5, not in 4.1.6.
+cp -r no-file junk 2>/dev/null || fail=1
+
+cp -r slink bar 2>/dev/null || fail=1
+set x $(ls -l bar); shift; mode=$1
+case $mode in
+ l*) ;;
+ *) fail=1;;
+esac
+
+Exit $fail
diff --git a/tests/cp/reflink-auto.sh b/tests/cp/reflink-auto.sh
new file mode 100755
index 0000000..dcf6e23
--- /dev/null
+++ b/tests/cp/reflink-auto.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Test cp --reflink=auto
+
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+cleanup_() { rm -rf "$other_partition_tmpdir"; }
+. "$abs_srcdir/tests/other-fs-tmpdir"
+a_other="$other_partition_tmpdir/a"
+rm -f "$a_other" || framework_failure_
+
+echo non_zero_size > "$a_other" || framework_failure_
+
+# we shouldn't be able to reflink() files on separate partitions
+returns_ 1 cp --reflink "$a_other" b || fail=1
+
+# --reflink=auto should fall back to a normal copy
+cp --reflink=auto "$a_other" b || fail=1
+test -s b || fail=1
+
+# --reflink=auto should allow --sparse for fallback copies.
+# This command can be used to create minimal sized copies.
+cp --reflink=auto --sparse=always "$a_other" b || fail=1
+test -s b || fail=1
+
+# --reflink=auto should be overridden by --reflink=never
+cp --reflink=auto --reflink=never "$a_other" b || fail=1
+test -s b || fail=1
+
+Exit $fail
diff --git a/tests/cp/reflink-perm.sh b/tests/cp/reflink-perm.sh
new file mode 100755
index 0000000..480076f
--- /dev/null
+++ b/tests/cp/reflink-perm.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Test cp --reflink copies permissions
+
+# Copyright (C) 2009-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+
+> time_check
+> file
+ts='2009-08-28 19:00'
+touch -d "$ts" file || framework_failure_
+test time_check -nt file || skip_ "The system clock is wrong"
+
+chmod a=rwx file || framework_failure_
+umask 077
+cp --reflink=auto --preserve file copy || fail=1
+
+mode=$(stat --printf "%A" copy)
+test "$mode" = "-rwxrwxrwx" || fail=1
+
+test copy -nt file && fail=1
+
+# Ensure that --attributes-only overrides --reflink completely
+echo > file2 # file with data
+cp --reflink=auto --preserve --attributes-only file2 empty_copy || fail=1
+compare /dev/null empty_copy || fail=1
+cp --reflink=always --preserve --attributes-only file2 empty_copy || fail=1
+compare /dev/null empty_copy || fail=1
+
+Exit $fail
diff --git a/tests/cp/same-file.sh b/tests/cp/same-file.sh
new file mode 100755
index 0000000..03f66ba
--- /dev/null
+++ b/tests/cp/same-file.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+# Test some of cp's options and how cp handles situations in
+# which a naive implementation might overwrite the source file.
+
+# Copyright (C) 1998-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# Unset CDPATH. Otherwise, output from the 'cd dir' command
+# can make this test fail.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+VERSION_CONTROL=numbered; export VERSION_CONTROL
+
+# Determine whether a hard link to a symlink points to the symlink
+# itself or to its referent. For example, the link from FreeBSD6.1
+# does dereference a symlink, but the one from Linux does not.
+ln -s no-such dangling-slink
+ln dangling-slink hard-link > /dev/null 2>&1 \
+ && hard_link_to_symlink_does_the_deref=no \
+ || hard_link_to_symlink_does_the_deref=yes
+rm -f no-such dangling-slink hard-link
+
+test $hard_link_to_symlink_does_the_deref = yes \
+ && remove_these_sed='/^0 -[bf]*l .*sl1 ->/d; /hlsl/d' \
+ || remove_these_sed='/^ELIDE NO TEST OUTPUT/d'
+
+exec 3>&1 1> actual
+
+# FIXME: This should be bigger: like more than 8k
+contents=XYZ
+
+for args in 'foo symlink' 'symlink foo' 'foo foo' 'sl1 sl2' \
+ 'foo hardlink' 'hlsl sl2'; do
+ for options in '' -d -f -df --rem -b -bd -bf -bdf \
+ -l -dl -fl -dfl -bl -bdl -bfl -bdfl -s -sf; do
+ case $args$options in
+ # These tests are not portable.
+ # They all involve making a hard link to a symbolic link.
+ # In the past, we've skipped the tests that are not portable,
+ # by doing "continue" here and eliminating the corresponding
+ # expected output lines below. Don't do that anymore.
+ 'symlink foo'-dfl)
+ continue;;
+ 'symlink foo'-bdl)
+ continue;;
+ 'symlink foo'-bdfl)
+ continue;;
+ 'sl1 sl2'-dfl)
+ continue;;
+ 'sl1 sl2'-bd*l)
+ continue;;
+ 'sl1 sl2'-dl)
+ continue;;
+ esac
+
+ # cont'd Instead, skip them only on systems for which link does
+ # dereference a symlink. Detect and skip such tests here.
+ case $hard_link_to_symlink_does_the_deref:$args:$options in
+ 'yes:sl1 sl2:-fl')
+ continue ;;
+ 'yes:sl1 sl2:-bl')
+ continue ;;
+ 'yes:sl1 sl2:-bfl')
+ continue ;;
+ yes:hlsl*)
+ continue ;;
+ esac
+
+ rm -rf dir
+ mkdir dir
+ cd dir
+ echo $contents > foo
+ case "$args" in *symlink*) ln -s foo symlink ;; esac
+ case "$args" in *hardlink*) ln foo hardlink ;; esac
+ case "$args" in *sl1*) ln -s foo sl1;; esac
+ case "$args" in *sl2*) ln -s foo sl2;; esac
+ case "$args" in *hlsl*) ln sl2 hlsl;; esac
+ (
+ (
+ # echo 1>&2 cp $options $args
+ cp $options $args 2>_err
+ echo $? $options
+
+ # Normalize the program name and diagnostics in the error output,
+ # and put brackets around the output.
+ if test -s _err; then
+ sed '
+ s/symbolic link/symlink/
+ s/^[^:]*:\([^:]*\).*/cp:\1/
+ 1s/^/[/
+ $s/$/]/
+ ' _err
+ fi
+ # Strip off all but the file names.
+ ls=$(ls -gG --ignore=_err . \
+ | sed \
+ -e '/^total /d' \
+ -e 's/^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *//')
+ echo "($ls)"
+ # Make sure the original is unchanged and that
+ # the destination is a copy.
+ for f in $args; do
+ if test -f $f; then
+ case "$(cat $f)" in
+ "$contents") ;;
+ *) echo cp FAILED;;
+ esac
+ else
+ echo symlink-loop
+ fi
+ done
+ ) | tr '\n' ' '
+ echo
+ ) | sed 's/ *$//'
+ cd ..
+ done
+ echo
+done
+
+cat <<\EOF | sed "$remove_these_sed" > expected
+1 [cp: 'foo' and 'symlink' are the same file] (foo symlink -> foo)
+1 -d [cp: 'foo' and 'symlink' are the same file] (foo symlink -> foo)
+1 -f [cp: 'foo' and 'symlink' are the same file] (foo symlink -> foo)
+1 -df [cp: 'foo' and 'symlink' are the same file] (foo symlink -> foo)
+0 --rem (foo symlink)
+0 -b (foo symlink symlink.~1~ -> foo)
+0 -bd (foo symlink symlink.~1~ -> foo)
+0 -bf (foo symlink symlink.~1~ -> foo)
+0 -bdf (foo symlink symlink.~1~ -> foo)
+1 -l [cp: cannot create hard link 'symlink' to 'foo'] (foo symlink -> foo)
+1 -dl [cp: cannot create hard link 'symlink' to 'foo'] (foo symlink -> foo)
+0 -fl (foo symlink)
+0 -dfl (foo symlink)
+0 -bl (foo symlink symlink.~1~ -> foo)
+0 -bdl (foo symlink symlink.~1~ -> foo)
+0 -bfl (foo symlink symlink.~1~ -> foo)
+0 -bdfl (foo symlink symlink.~1~ -> foo)
+1 -s [cp: cannot create symlink 'symlink' to 'foo'] (foo symlink -> foo)
+0 -sf (foo symlink -> foo)
+
+1 [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 -d [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 -f [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 -df [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 --rem [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 -b [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+0 -bd (foo -> foo foo.~1~ symlink -> foo) symlink-loop symlink-loop
+1 -bf [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+0 -bdf (foo -> foo foo.~1~ symlink -> foo) symlink-loop symlink-loop
+0 -l (foo symlink -> foo)
+0 -dl (foo symlink -> foo)
+0 -fl (foo symlink -> foo)
+0 -bl (foo symlink -> foo)
+0 -bfl (foo symlink -> foo)
+1 -s [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+1 -sf [cp: 'symlink' and 'foo' are the same file] (foo symlink -> foo)
+
+1 [cp: 'foo' and 'foo' are the same file] (foo)
+1 -d [cp: 'foo' and 'foo' are the same file] (foo)
+1 -f [cp: 'foo' and 'foo' are the same file] (foo)
+1 -df [cp: 'foo' and 'foo' are the same file] (foo)
+1 --rem [cp: 'foo' and 'foo' are the same file] (foo)
+1 -b [cp: 'foo' and 'foo' are the same file] (foo)
+1 -bd [cp: 'foo' and 'foo' are the same file] (foo)
+0 -bf (foo foo.~1~)
+0 -bdf (foo foo.~1~)
+0 -l (foo)
+0 -dl (foo)
+0 -fl (foo)
+0 -dfl (foo)
+0 -bl (foo)
+0 -bdl (foo)
+0 -bfl (foo foo.~1~)
+0 -bdfl (foo foo.~1~)
+1 -s [cp: 'foo' and 'foo' are the same file] (foo)
+1 -sf [cp: 'foo' and 'foo' are the same file] (foo)
+
+1 [cp: 'sl1' and 'sl2' are the same file] (foo sl1 -> foo sl2 -> foo)
+0 -d (foo sl1 -> foo sl2 -> foo)
+1 -f [cp: 'sl1' and 'sl2' are the same file] (foo sl1 -> foo sl2 -> foo)
+0 -df (foo sl1 -> foo sl2 -> foo)
+0 --rem (foo sl1 -> foo sl2)
+0 -b (foo sl1 -> foo sl2 sl2.~1~ -> foo)
+0 -bd (foo sl1 -> foo sl2 -> foo sl2.~1~ -> foo)
+0 -bf (foo sl1 -> foo sl2 sl2.~1~ -> foo)
+0 -bdf (foo sl1 -> foo sl2 -> foo sl2.~1~ -> foo)
+1 -l [cp: cannot create hard link 'sl2' to 'sl1'] (foo sl1 -> foo sl2 -> foo)
+0 -fl (foo sl1 -> foo sl2)
+0 -bl (foo sl1 -> foo sl2 sl2.~1~ -> foo)
+0 -bfl (foo sl1 -> foo sl2 sl2.~1~ -> foo)
+1 -s [cp: cannot create symlink 'sl2' to 'sl1'] (foo sl1 -> foo sl2 -> foo)
+0 -sf (foo sl1 -> foo sl2 -> sl1)
+
+1 [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+1 -d [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+1 -f [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+1 -df [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+0 --rem (foo hardlink)
+0 -b (foo hardlink hardlink.~1~)
+0 -bd (foo hardlink hardlink.~1~)
+0 -bf (foo hardlink hardlink.~1~)
+0 -bdf (foo hardlink hardlink.~1~)
+0 -l (foo hardlink)
+0 -dl (foo hardlink)
+0 -fl (foo hardlink)
+0 -dfl (foo hardlink)
+0 -bl (foo hardlink)
+0 -bdl (foo hardlink)
+0 -bfl (foo hardlink)
+0 -bdfl (foo hardlink)
+1 -s [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+1 -sf [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
+
+1 [cp: 'hlsl' and 'sl2' are the same file] (foo hlsl -> foo sl2 -> foo)
+0 -d (foo hlsl -> foo sl2 -> foo)
+1 -f [cp: 'hlsl' and 'sl2' are the same file] (foo hlsl -> foo sl2 -> foo)
+0 -df (foo hlsl -> foo sl2 -> foo)
+0 --rem (foo hlsl -> foo sl2)
+0 -b (foo hlsl -> foo sl2 sl2.~1~ -> foo)
+0 -bd (foo hlsl -> foo sl2 -> foo sl2.~1~ -> foo)
+0 -bf (foo hlsl -> foo sl2 sl2.~1~ -> foo)
+0 -bdf (foo hlsl -> foo sl2 -> foo sl2.~1~ -> foo)
+1 -l [cp: cannot create hard link 'sl2' to 'hlsl'] (foo hlsl -> foo sl2 -> foo)
+0 -dl (foo hlsl -> foo sl2 -> foo)
+0 -fl (foo hlsl -> foo sl2)
+0 -dfl (foo hlsl -> foo sl2 -> foo)
+0 -bl (foo hlsl -> foo sl2 sl2.~1~ -> foo)
+0 -bdl (foo hlsl -> foo sl2 -> foo)
+0 -bfl (foo hlsl -> foo sl2 sl2.~1~ -> foo)
+0 -bdfl (foo hlsl -> foo sl2 -> foo)
+1 -s [cp: cannot create symlink 'sl2' to 'hlsl'] (foo hlsl -> foo sl2 -> foo)
+0 -sf (foo hlsl -> foo sl2 -> hlsl)
+
+EOF
+
+exec 1>&3 3>&-
+
+compare expected actual 1>&2 || fail=1
+
+Exit $fail
diff --git a/tests/cp/slink-2-slink.sh b/tests/cp/slink-2-slink.sh
new file mode 100755
index 0000000..00e3fee
--- /dev/null
+++ b/tests/cp/slink-2-slink.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# 'test cp --update A B' where A and B are both symlinks that point
+# to the same file
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+touch file || framework_failure_
+ln -s file a || framework_failure_
+ln -s file b || framework_failure_
+ln -s no-such-file c || framework_failure_
+ln -s no-such-file d || framework_failure_
+
+cp --update --no-dereference a b || fail=1
+cp --update --no-dereference c d || fail=1
+
+Exit $fail
diff --git a/tests/cp/sparse-2.sh b/tests/cp/sparse-2.sh
new file mode 100755
index 0000000..5d3c5ec
--- /dev/null
+++ b/tests/cp/sparse-2.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Exercise a few more corners of the copying code.
+
+# Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp dd
+
+touch sparse_chk
+seek_data_capable_ sparse_chk \
+ || skip_ "insufficient SEEK_DATA support"
+
+# Exercise the code that handles a file ending in a hole.
+printf x > k || framework_failure_
+dd bs=1k seek=128 of=k < /dev/null || framework_failure_
+
+# The first time through the outer loop, the input file, K, ends with a hole.
+# The second time through, we append a byte so that it does not.
+for append in no yes; do
+ test $append = yes && printf y >> k
+ for i in always never; do
+ cp --reflink=never --sparse=$i k k2 || fail=1
+ cmp k k2 || fail=1
+ done
+done
+
+# Ensure that --sparse=always can restore holes.
+rm -f k
+# Create a file starting with an "x", followed by 256K-1 0 bytes.
+printf x > k || framework_failure_
+dd bs=1k seek=1 of=k count=255 < /dev/zero || framework_failure_
+
+# cp should detect the all-zero blocks and convert some of them to holes.
+cp --debug --reflink=never --sparse=always k k2 >cp.out || fail=1
+cmp k k2 || fail=1
+grep 'sparse detection: .*zeros' cp.out || { cat cp.out; fail=1; }
+
+# cp should disable reflink AND copy offload with --sparse=never
+cp --debug --sparse=never k k2 >cp.out || fail=1
+cmp k k2 || fail=1
+grep 'copy offload: avoided, reflink: no' cp.out || { cat cp.out; fail=1; }
+
+Exit $fail
diff --git a/tests/cp/sparse-extents-2.sh b/tests/cp/sparse-extents-2.sh
new file mode 100755
index 0000000..575dbbf
--- /dev/null
+++ b/tests/cp/sparse-extents-2.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+# Test cp --sparse=always through SEEK_DATA copy
+
+# Copyright (C) 2010-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_perl_
+
+# The test was seen to fail on ext3 so exclude that type
+# (or any file system where the type can't be determined)
+touch sparse_chk
+if seek_data_capable_ sparse_chk && ! df -t ext3 . >/dev/null; then
+ : # Current partition has working extents. Good!
+else
+ skip_ "insufficient SEEK_DATA support"
+
+ # It's not; we need to create one, hence we need root access.
+ require_root_
+
+ cwd=$PWD
+ cleanup_() { cd /; umount "$cwd/mnt"; }
+
+ skip=0
+ # Create an ext4 loopback file system
+ dd if=/dev/zero of=blob bs=32k count=1000 || skip=1
+ mkdir mnt
+ mkfs -t ext4 -F blob ||
+ skip_ "failed to create ext4 file system"
+ mount -oloop blob mnt || skip=1
+ cd mnt || skip=1
+ echo test > f || skip=1
+ test -s f || skip=1
+
+ test $skip = 1 &&
+ skip_ "insufficient mount/ext4 support"
+fi
+
+# =================================================
+# The data below was set up to ensure that the original FIEMAP-copying code
+# was exercised enough to provoke at least two iterations of the do...while loop
+# in which it calls ioctl (fd, FS_IOC_FIEMAP,...
+# This also verifies that non-trivial extents are preserved.
+
+# Extract logical block number and length pairs from filefrag -v output.
+# The initial sed is to remove the "eof" from the normally-empty "flags" field.
+# Similarly, remove flags values like "unknown,delalloc,eof".
+# That is required when that final extent has no number in the "expected" field.
+f()
+{
+ sed 's/ [a-z,][a-z,]*$//' $@ \
+ | $AWK '/^ *[0-9]/ {printf "%d %d ", $2, (NF>=6 ? $6 : (NF<5 ? $NF : $5)) }
+ END {print ""}'
+}
+
+for i in $(seq 1 2 21); do
+ for j in 1 2 31 100; do
+ $PERL -e '$n = '$i' * 1024; *F = *STDOUT;' \
+ -e 'for (1..'$j') { sysseek (*F, $n, 1)' \
+ -e '&& syswrite (*F, chr($_)x$n) or die "$!"}' > j1 || fail=1
+
+ # Note there is an implicit sync performed by cp on Linux kernels
+ # before 2.6.39 to work around bugs in EXT4 and BTRFS.
+ # (this was removed in the release after coreutils-8.32).
+ # Note also the -s parameter to the filefrag commands below
+ # for the same reasons.
+ cp --reflink=never --sparse=always j1 j2 || fail=1
+
+ cmp j1 j2 || fail_ "data loss i=$i j=$j"
+ if ! filefrag -vs j1 | grep -F extent >/dev/null; then
+ test $skip != 1 && warn_ 'skipping part; you lack filefrag'
+ skip=1
+ else
+ # Here is sample filefrag output:
+ # $ perl -e 'BEGIN{$n=16*1024; *F=*STDOUT}' \
+ # -e 'for (1..5) { sysseek(*F,$n,1)' \
+ # -e '&& syswrite *F,"."x$n or die "$!"}' > j
+ # $ filefrag -v j
+ # File system type is: ef53
+ # File size of j is 163840 (40 blocks, blocksize 4096)
+ # ext logical physical expected length flags
+ # 0 4 6258884 4
+ # 1 12 6258892 6258887 4
+ # 2 20 6258900 6258895 4
+ # 3 28 6258908 6258903 4
+ # 4 36 6258916 6258911 4 eof
+ # j: 6 extents found
+
+ # exclude the physical block numbers; they always differ
+ filefrag -v j1 > ff1 || framework_failure_
+ filefrag -vs j2 > ff2 || framework_failure_
+ { f ff1; f ff2; } | $PERL $abs_srcdir/tests/filefrag-extent-compare \
+ || {
+ warn_ ignoring filefrag-reported extent map differences
+ # Show the differing extent maps.
+ head -n99 ff1 ff2
+ }
+ fi
+ test $fail = 1 && break 2
+ done
+done
+
+Exit $fail
diff --git a/tests/cp/sparse-extents.sh b/tests/cp/sparse-extents.sh
new file mode 100755
index 0000000..5009f09
--- /dev/null
+++ b/tests/cp/sparse-extents.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+# Test cp handles extents correctly
+
+# Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_sparse_support_
+
+touch sparse_chk || framework_failure_
+seek_data_capable_ sparse_chk ||
+ skip_ 'insufficient SEEK_DATA support'
+
+fallocate --help >/dev/null || skip_ 'The fallocate utility is required'
+touch falloc.test || framework_failure_
+fallocate -l 1 -o 1 -n falloc.test ||
+ skip_ 'this file system lacks FALLOCATE support'
+rm falloc.test
+
+# We don't currently handle unwritten extents specially
+if false; then
+# Require more space than we'll actually use, so that
+# tests run in parallel do not run out of space.
+# Otherwise, with inadequate space, simply running the following
+# fallocate command would induce a temporary file-system-full condition,
+# which would cause failure of unrelated tests run in parallel.
+require_file_system_bytes_free_ 800000000
+
+fallocate -l 1MiB num.test ||
+ skip_ "this fallocate doesn't support numbers with IEX suffixes"
+
+fallocate -l 600MiB space.test ||
+ skip_ 'this test needs at least 600MiB free space'
+
+# Disable this test on old BTRFS (e.g. Fedora 14)
+# which reports ordinary extents for unwritten ones.
+filefrag space.test || skip_ 'the 'filefrag' utility is missing'
+filefrag -v space.test | grep -F 'unwritten' > /dev/null ||
+ skip_ 'this file system does not report empty extents as "unwritten"'
+
+rm space.test
+
+# Ensure we read a large empty file quickly
+fallocate -l 300MiB empty.big || framework_failure_
+timeout 3 cp --reflink=never --sparse=always empty.big cp.test || fail=1
+test $(stat -c %s empty.big) = $(stat -c %s cp.test) || fail=1
+rm empty.big cp.test
+fi
+
+# Ensure we handle extents beyond file size correctly.
+# Note until we support fallocate, we will not maintain
+# the file allocation. FIXME: amend this test if fallocate is supported.
+# Note currently this only uses SEEK_DATA logic when the allocation (-l)
+# is smaller than the size, thus identifying the file as sparse.
+# Note the '-l 1' case is an effective noop, and just checks
+# a file with a trailing hole is copied correctly.
+for sparse_arg in always auto never; do
+ for alloc in '-l 4194304' '-l 1048576 -o 4194304' '-l 1'; do
+ dd count=10 if=/dev/urandom iflag=fullblock of=unwritten.withdata
+ truncate -s 2MiB unwritten.withdata || framework_failure_
+ fallocate $alloc -n unwritten.withdata || framework_failure_
+ cp --reflink=never --sparse=$sparse_arg unwritten.withdata cp.test || fail=1
+ test $(stat -c %s unwritten.withdata) = $(stat -c %s cp.test) || fail=1
+ cmp unwritten.withdata cp.test || fail=1
+ rm unwritten.withdata cp.test || framework_failure_
+ done
+done
+
+Exit $fail
diff --git a/tests/cp/sparse-perf.sh b/tests/cp/sparse-perf.sh
new file mode 100755
index 0000000..793ecee
--- /dev/null
+++ b/tests/cp/sparse-perf.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# ensure that a sparse file is copied efficiently, by default
+
+# Copyright (C) 2021-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+# Create a large-but-sparse file.
+timeout 10 truncate -s1T f ||
+ skip_ "unable to create a 1 TiB sparse file"
+
+# Note zfs with zfs_dmu_offset_next_sync=0 (the default)
+# will generally skip here, due to needing about 5 seconds
+# between the creation of the file and the use of SEEK_DATA,
+# for it to determine it's an empty file (return ENXIO).
+seek_data_capable_ f ||
+ skip_ "insufficient SEEK_DATA support"
+
+# Nothing can read that many bytes in so little time.
+timeout 10 cp --reflink=never f f2 || fail=1
+
+# Ensure that the sparse file copied through SEEK_DATA has the same size
+# in bytes as the original.
+test "$(stat --printf %s f)" = "$(stat --printf %s f2)" || fail=1
+
+Exit $fail
diff --git a/tests/cp/sparse-to-pipe.sh b/tests/cp/sparse-to-pipe.sh
new file mode 100755
index 0000000..a33a7b3
--- /dev/null
+++ b/tests/cp/sparse-to-pipe.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# copy a sparse file to a pipe, to exercise some seldom-used parts of copy.c
+
+# Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+require_sparse_support_
+
+# Terminate any background cp process
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+mkfifo_or_skip_ pipe
+timeout 10 cat pipe > copy & pid=$!
+
+truncate -s1M sparse || framework_failure_
+timeout 10 cp sparse pipe || fail=1
+
+# Ensure that the cat has completed before comparing.
+wait $pid
+
+cmp sparse copy || fail=1
+
+Exit $fail
diff --git a/tests/cp/sparse.sh b/tests/cp/sparse.sh
new file mode 100755
index 0000000..12a4380
--- /dev/null
+++ b/tests/cp/sparse.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Test cp --sparse=always
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_sparse_support_
+
+# Create a sparse file.
+# It has to be at least 128K in order to be sparse on some systems.
+# Make its size one larger than 128K, in order to tickle the
+# bug in coreutils-6.0.
+size=$(expr 128 \* 1024 + 1)
+dd bs=1 seek=$size of=sparse < /dev/null 2> /dev/null || framework_failure_
+
+# Avoid reflinking. We want to test hole navigation here.
+cp_no_reflink() {
+ cp --reflink=never "$@"
+}
+
+cp_no_reflink --sparse=always sparse copy || fail=1
+
+# Ensure that the copy has the same block count as the original.
+test $(stat --printf %b copy) -le $(stat --printf %b sparse) || fail=1
+
+# Ensure that --sparse={always,never} with --reflink fail.
+returns_ 1 cp --sparse=always --reflink sparse copy || fail=1
+returns_ 1 cp --sparse=never --reflink sparse copy || fail=1
+
+
+# Ensure we handle sparse/non-sparse transitions correctly
+maxn=128 # how many $hole_size chunks per file
+hole_size=$(stat -c %o copy)
+dd if=/dev/zero bs=$hole_size count=$maxn of=zeros || framework_failure_
+tr '\0' 'U' < zeros > nonzero || framework_failure_
+
+for pattern in 1 0; do
+ test "$pattern" = 1 && pattern="$(printf '%s\n%s' nonzero zeros)"
+ test "$pattern" = 0 && pattern="$(printf '%s\n%s' zeros nonzero)"
+
+ for n in 1 2 4 11 32 $maxn; do
+ parts=$(expr $maxn / $n)
+
+ rm -f file.in
+
+ # Generate non sparse file for copying with alternating
+ # hole/data patterns of size n * $hole_size
+ for i in $(yes "$pattern" | head -n$parts); do
+ dd iflag=fullblock if=$i of=file.in conv=notrunc oflag=append \
+ bs=$hole_size count=$n status=none || framework_failure_
+ done
+
+ cp_no_reflink --sparse=always file.in sparse.out || fail=1 # non sparse in
+ cp_no_reflink --sparse=always sparse.out sparse.out2 || fail=1 # sparse in
+
+ cmp file.in sparse.out || fail=1
+ cmp file.in sparse.out2 || fail=1
+
+ ls -lsh file.in sparse.*
+ done
+done
+
+Exit $fail
diff --git a/tests/cp/special-bits.sh b/tests/cp/special-bits.sh
new file mode 100755
index 0000000..a9288f5
--- /dev/null
+++ b/tests/cp/special-bits.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# make sure 'cp -p' preserves special bits
+# This works only when run as root.
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# This test would fail due to a bug introduced in 4.0y.
+# The bug was fixed in 4.0z.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+require_root_
+
+touch a b c || framework_failure_
+chmod u+sx,go= a || framework_failure_
+chmod u=rwx,g=sx,o= b || framework_failure_
+chmod a=r,ug+sx c || framework_failure_
+chown $NON_ROOT_USERNAME . || framework_failure_
+chmod u=rwx,g=rx,o=rx . || framework_failure_
+
+
+cp -p a a2 || fail=1
+set _ $(ls -l a); shift; p1=$1
+set _ $(ls -l a2); shift; p2=$1
+test $p1 = $p2 || fail=1
+
+cp -p b b2 || fail=1
+set _ $(ls -l b); shift; p1=$1
+set _ $(ls -l b2); shift; p2=$1
+test $p1 = $p2 || fail=1
+
+chroot --skip-chdir --user=$NON_ROOT_USERNAME / env PATH="$PATH" cp -p c c2 \
+ || fail=1
+set _ $(ls -l c); shift; p1=$1
+set _ $(ls -l c2); shift; p2=$1
+test $p1 = $p2 && fail=1
+
+Exit $fail
diff --git a/tests/cp/special-f.sh b/tests/cp/special-f.sh
new file mode 100755
index 0000000..ed70d7a
--- /dev/null
+++ b/tests/cp/special-f.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Ensure that "cp -Rf fifo E" unlinks E and retries.
+# Up until coreutils-6.10.171, it would not.
+
+# Copyright (C) 2008-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkfifo_or_skip_ fifo
+
+touch e || framework-failure
+
+for force in '' '-f'; do
+ # Second time through will need to unlink fifo e
+ timeout 10 cp -R $force fifo e || fail=1
+ test -p fifo || fail=1
+done
+
+Exit $fail
diff --git a/tests/cp/src-base-dot.sh b/tests/cp/src-base-dot.sh
new file mode 100755
index 0000000..70f8921
--- /dev/null
+++ b/tests/cp/src-base-dot.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Ensure that "mkdir x y; cd y; cp -ab ../x/. ." is a successful, silent, no-op.
+
+# Copyright (C) 2006-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir x y || framework_failure_
+
+cd y
+cp --verbose -ab ../x/. . > out 2>&1 || fail=1
+compare /dev/null out || fail=1
+
+Exit $fail
diff --git a/tests/cp/symlink-slash.sh b/tests/cp/symlink-slash.sh
new file mode 100755
index 0000000..5a8cb4f
--- /dev/null
+++ b/tests/cp/symlink-slash.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Make sure that cp -dR dereferences a symlink arg if its name is
+# written with a trailing slash.
+
+# Copyright (C) 2000-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir dir || framework_failure_
+ln -s dir symlink || framework_failure_
+
+cp -dR symlink/ s || fail=1
+set $(ls -l s)
+
+# Prior to fileutils-4.0q, the following would have output ...'s -> dir'
+# because the trailing slash was removed unconditionally (now you have to
+# use the new --strip-trailing-slash option) causing cp to reproduce the
+# symlink. Now, the trailing slash is interpreted by the stat library
+# call and so cp ends up dereferencing the symlink and copying the directory.
+test "$*" = 'total 0' && : || fail=1
+
+Exit $fail
diff --git a/tests/cp/thru-dangling.sh b/tests/cp/thru-dangling.sh
new file mode 100755
index 0000000..1cbb12d
--- /dev/null
+++ b/tests/cp/thru-dangling.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Ensure that cp works as documented, when the destination is a dangling symlink
+
+# Copyright (C) 2007-2023 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+ln -s no-such dangle || framework_failure_
+echo hi > f || framework_failure_
+echo hi > exp || framework_failure_
+echo "cp: not writing through dangling symlink 'dangle'" \
+ > exp-err || framework_failure_
+
+
+# Starting with 6.9.90, this usage fails, by default:
+for opt in '' '-f'; do
+ returns_ 1 cp $opt f dangle > err 2>&1 || fail=1
+ compare exp-err err || fail=1
+ test -f no-such && fail=1
+done
+
+
+# But you can set POSIXLY_CORRECT to get the historical behavior.
+env POSIXLY_CORRECT=1 cp f dangle > out 2>&1 || fail=1
+cat no-such >> out || fail=1
+compare exp out || fail=1
+
+
+# Starting with 8.30 we treat ELOOP as existing and so
+# remove the symlink
+ln -s loop loop || framework_failure_
+cp -f f loop > err 2>&1 || fail=1
+compare /dev/null err || fail=1
+compare exp loop || fail=1
+test -f loop || fail=1
+
+Exit $fail